diff --git a/.babelrc b/.babelrc deleted file mode 100644 index d8a888ed2..000000000 --- a/.babelrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presets": ["es2015"], - "compact": true, - "minified": true, - "only": "public/themes/pterodactyl/js/frontend/files/src/*.js", - "sourceMaps": "inline", - "comments": false -} diff --git a/.dev/docker/README.md b/.dev/docker/README.md deleted file mode 100644 index c5c2991c7..000000000 --- a/.dev/docker/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Pterodactyl Panel - Docker Image -This is a ready to use docker image for the panel. - -## Requirements -This docker image requires some additional software to function. The software can either be provided in other containers (see the [docker-compose.yml](docker-compose.yml) as an example) or as existing instances. - -A mysql database is required. We recommend the stock [MariaDB Image](https://hub.docker.com/_/mariadb/) image if you prefer to run it in a docker container. As a non-containerized option we recommend mariadb. - -A caching software is required as well. We recommend the stock [Redis Image](https://hub.docker.com/_/redis/) image. You can choose any of the [supported options](#cache-drivers). - -You can provide additional settings using a custom `.env` file or by setting the appropriate environment variables in the docker-compose file. - -## Setup - -Start the docker container and the required dependencies (either provide existing ones or start containers as well, see the [docker-compose.yml](docker-compose.yml) file as an example). - -After the startup is complete you'll need to create a user. -If you are running the docker container without docker-compose, use: -``` -docker exec -it php artisan p:user:make -``` -If you are using docker compose use -``` -docker-compose exec panel php artisan p:user:make -``` - -## Environment Variables -There are multiple environment variables to configure the panel when not providing your own `.env` file, see the following table for details on each available option. - -Note: If your `APP_URL` starts with `https://` you need to provide an `LETSENCRYPT_EMAIL` as well so Certificates can be generated. - -| Variable | Description | Required | -| ------------------- | ------------------------------------------------------------------------------ | -------- | -| `APP_URL` | The URL the panel will be reachable with (including protocol) | yes | -| `APP_TIMEZONE` | The timezone to use for the panel | yes | -| `LETSENCRYPT_EMAIL` | The email used for letsencrypt certificate generation | yes | -| `DB_HOST` | The host of the mysql instance | yes | -| `DB_PORT` | The port of the mysql instance | yes | -| `DB_DATABASE` | The name of the mysql database | yes | -| `DB_USERNAME` | The mysql user | yes | -| `DB_PASSWORD` | The mysql password for the specified user | yes | -| `CACHE_DRIVER` | The cache driver (see [Cache drivers](#cache-drivers) for detais) | yes | -| `SESSION_DRIVER` | | yes | -| `QUEUE_DRIVER` | | yes | -| `REDIS_HOST` | The hostname or IP address of the redis database | yes | -| `REDIS_PASSWORD` | The password used to secure the redis database | maybe | -| `REDIS_PORT` | The port the redis database is using on the host | maybe | -| `MAIL_DRIVER` | The email driver (see [Mail drivers](#mail-drivers) for details) | yes | -| `MAIL_FROM` | The email that should be used as the sender email | yes | -| `MAIL_HOST` | The host of your mail driver instance | maybe | -| `MAIL_PORT` | The port of your mail driver instance | maybe | -| `MAIL_USERNAME` | The username for your mail driver | maybe | -| `MAIL_PASSWORD` | The password for your mail driver | maybe | - - -### Cache drivers -You can choose between different cache drivers depending on what you prefer. -We recommend redis when using docker as it can be started in a container easily. - -| Driver | Description | Required variables | -| -------- | ------------------------------------ | ------------------------------------------------------ | -| redis | host where redis is running | `REDIS_HOST` | -| redis | port redis is running on | `REDIS_PORT` | -| redis | redis database password | `REDIS_PASSWORD` | - -### Mail drivers -You can choose between different mail drivers according to your needs. -Every driver requires `MAIL_FROM` to be set. - -| Driver | Description | Required variables | -| -------- | ------------------------------------ | ------------------------------------------------------------- | -| mail | uses the installed php mail | | -| mandrill | [Mandrill](http://www.mandrill.com/) | `MAIL_USERNAME` | -| postmark | [Postmark](https://postmarkapp.com/) | `MAIL_USERNAME` | -| mailgun | [Mailgun](https://www.mailgun.com/) | `MAIL_USERNAME`, `MAIL_HOST` | -| smtp | Any SMTP server can be configured | `MAIL_USERNAME`, `MAIL_HOST`, `MAIL_PASSWORD`, `MAIL_PORT` | \ No newline at end of file diff --git a/.dev/docker/default.conf b/.dev/docker/default.conf deleted file mode 100644 index 0944bf799..000000000 --- a/.dev/docker/default.conf +++ /dev/null @@ -1,51 +0,0 @@ -# If using Ubuntu this file should be placed in: -# /etc/nginx/sites-available/ -# -# If using CentOS this file should be placed in: -# /etc/nginx/conf.d/ -# -server { - listen 80; - server_name _; - - root /app/public; - index index.html index.htm index.php; - charset utf-8; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location = /favicon.ico { access_log off; log_not_found off; } - location = /robots.txt { access_log off; log_not_found off; } - - access_log off; - error_log /var/log/nginx/pterodactyl.app-error.log error; - - # allow larger file uploads and longer script runtimes - client_max_body_size 100m; - client_body_timeout 120s; - - sendfile off; - - location ~ \.php$ { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - # the fastcgi_pass path needs to be changed accordingly when using CentOS - fastcgi_pass unix:/var/run/php/php-fpm7.2.sock; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTP_PROXY ""; - fastcgi_intercept_errors off; - fastcgi_buffer_size 16k; - fastcgi_buffers 4 16k; - fastcgi_connect_timeout 300; - fastcgi_send_timeout 300; - fastcgi_read_timeout 300; - } - - location ~ /\.ht { - deny all; - } -} diff --git a/.dev/docker/default_ssl.conf b/.dev/docker/default_ssl.conf deleted file mode 100644 index c2c2b6df0..000000000 --- a/.dev/docker/default_ssl.conf +++ /dev/null @@ -1,70 +0,0 @@ -# If using Ubuntu this file should be placed in: -# /etc/nginx/sites-available/ -# -server { - listen 80; - server_name ; - return 301 https://$server_name$request_uri; -} - -server { - listen 443 ssl http2; - server_name ; - - root /app/public; - index index.php; - - access_log /var/log/nginx/pterodactyl.app-access.log; - error_log /var/log/nginx/pterodactyl.app-error.log error; - - # allow larger file uploads and longer script runtimes - client_max_body_size 100m; - client_body_timeout 120s; - - sendfile off; - - # strengthen ssl security - ssl_certificate /etc/letsencrypt/live//fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live//privkey.pem; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - ssl_session_cache shared:SSL:10m; - ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; - - # See the link below for more SSL information: - # https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html - # - # ssl_dhparam /etc/ssl/certs/dhparam.pem; - - # Add headers to serve security related headers - add_header Strict-Transport-Security "max-age=15768000; preload;"; - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header X-Robots-Tag none; - add_header Content-Security-Policy "frame-ancestors 'self'"; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location ~ \.php$ { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass unix:/var/run/php/php-fpm7.2.sock; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTP_PROXY ""; - fastcgi_intercept_errors off; - fastcgi_buffer_size 16k; - fastcgi_buffers 4 16k; - fastcgi_connect_timeout 300; - fastcgi_send_timeout 300; - fastcgi_read_timeout 300; - include /etc/nginx/fastcgi_params; - } - - location ~ /\.ht { - deny all; - } -} \ No newline at end of file diff --git a/.dev/docker/entrypoint.sh b/.dev/docker/entrypoint.sh deleted file mode 100644 index c2b58375a..000000000 --- a/.dev/docker/entrypoint.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/ash -## Ensure we are in /app - -cd /app - -mkdir -p /var/log/panel/logs/ /var/log/supervisord/ /var/log/nginx/ /var/log/php7/ \ -&& rmdir /app/storage/logs/ \ -&& chmod 777 /var/log/panel/logs/ \ -&& ln -s /var/log/panel/logs/ /app/storage/ - -## check for .env file and generate app keys if missing -if [ -f /app/var/.env ]; then - echo "external vars exist." - rm /app/.env - - ln -s /app/var/.env /app/ -else - echo "external vars don't exist." - rm /app/.env - touch /app/var/.env - - ## manually generate a key because key generate --force fails - echo -e "Generating key." - APP_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) - echo -e "Generated app key: $APP_KEY" - echo -e "APP_KEY=$APP_KEY" > /app/var/.env - - ln -s /app/var/.env /app/ -fi - -echo "Checking if https is required." -if [ -f /etc/nginx/conf.d/default.conf ]; then - echo "Using nginx config already in place." -else - echo "Checking if letsencrypt email is set." - if [ -z $LE_EMAIL ]; then - echo "No letsencrypt email is set Failing to http." - cp .dev/docker/default.conf /etc/nginx/conf.d/default.conf - - else - echo "writing ssl config" - cp .dev/docker/default_ssl.conf /etc/nginx/conf.d/default.conf - echo "updating ssl config for domain" - sed -i "s||$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/conf.d/default.conf - echo "generating certs" - certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n - fi -fi - -## check for DB up before starting the panel -echo "Checking database status." -until nc -z -v -w30 $DB_HOST 3306 - -do - echo "Waiting for database connection..." - # wait for 5 seconds before check again - sleep 5 -done - -## make sure the db is set up -echo -e "Migrating and Seeding D.B" -php artisan migrate --force -php artisan db:seed --force - -## start cronjobs for the queue -echo -e "Starting cron jobs." -crond -L /var/log/crond -l 5 - -echo -e "Starting supervisord." -exec "$@" \ No newline at end of file diff --git a/.dev/docker/supervisord.conf b/.dev/docker/supervisord.conf deleted file mode 100644 index f2fd3a1b5..000000000 --- a/.dev/docker/supervisord.conf +++ /dev/null @@ -1,39 +0,0 @@ -[unix_http_server] -file=/tmp/supervisor.sock ; path to your socket file - -[supervisord] -logfile=/var/log/supervisord/supervisord.log ; supervisord log file -logfile_maxbytes=50MB ; maximum size of logfile before rotation -logfile_backups=2 ; number of backed up logfiles -loglevel=error ; info, debug, warn, trace -pidfile=/var/run/supervisord.pid ; pidfile location -nodaemon=false ; run supervisord as a daemon -minfds=1024 ; number of startup file descriptors -minprocs=200 ; number of process descriptors -user=root ; default user -childlogdir=/var/log/supervisord/ ; where child log files will live - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface - -[supervisorctl] -serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket - -[program:php-fpm] -command=/usr/sbin/php-fpm7 -F -autostart=true -autorestart=true - -[program:queue-worker] -command=/usr/bin/php /app/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3 -user=nginx -autostart=true -autorestart=true - -[program:nginx] -command=/usr/sbin/nginx -g 'daemon off;' -autostart=true -autorestart=true -priority=10 -stdout_events_enabled=true -stderr_events_enabled=true \ No newline at end of file diff --git a/.dev/docker/www.conf b/.dev/docker/www.conf deleted file mode 100644 index 88142564c..000000000 --- a/.dev/docker/www.conf +++ /dev/null @@ -1,16 +0,0 @@ -[pterodactyl] - -user = nginx -group = nginx - -listen = /var/run/php/php-fpm7.2.sock -listen.owner = nginx -listen.group = nginx -listen.mode = 0750 - -pm = ondemand -pm.max_children = 9 -pm.process_idle_timeout = 10s -pm.max_requests = 200 - -clear_env = no \ No newline at end of file diff --git a/.dev/vagrant/.env.vagrant b/.dev/vagrant/.env.vagrant deleted file mode 100644 index 2427ec04e..000000000 --- a/.dev/vagrant/.env.vagrant +++ /dev/null @@ -1,39 +0,0 @@ -APP_ENV=develop -APP_DEBUG=true -APP_KEY=SomeRandomString3232RandomString -APP_THEME=pterodactyl -APP_TIMEZONE=UTC -APP_CLEAR_TASKLOG=720 -APP_DELETE_MINUTES=10 -APP_URL=http://192.168.50.2/ - -DB_HOST=localhost -DB_PORT=3306 -DB_DATABASE=panel -DB_USERNAME=pterodactyl -DB_PASSWORD=pterodactyl - -CACHE_DRIVER=memcached -MEMCACHED_HOST=127.0.0.1 -SESSION_DRIVER=database - -MAIL_DRIVER=smtp -MAIL_HOST=127.0.0.1 -MAIL_PORT=1025 -MAIL_USERNAME= -MAIL_PASSWORD= -MAIL_ENCRYPTION= -MAIL_FROM=support@pterodactyl.io - -API_PREFIX=api -API_VERSION=v1 -API_DEBUG=true - -QUEUE_DRIVER=database -QUEUE_HIGH=high -QUEUE_STANDARD=standard -QUEUE_LOW=low - -SQS_KEY=aws-public -SQS_SECRET=aws-secret -SQS_QUEUE_PREFIX=aws-queue-prefix diff --git a/.dev/vagrant/mailhog.service b/.dev/vagrant/mailhog.service deleted file mode 100644 index 01334183d..000000000 --- a/.dev/vagrant/mailhog.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Mailhog - -[Service] -# On some systems the user and group might be different. -# Some systems use `apache` as the user and group. -User=www-data -Group=www-data -Restart=on-failure -ExecStart=/usr/bin/mailhog - -[Install] -WantedBy=multi-user.target diff --git a/.dev/vagrant/mariadb.cnf b/.dev/vagrant/mariadb.cnf deleted file mode 100644 index 48b31ed8b..000000000 --- a/.dev/vagrant/mariadb.cnf +++ /dev/null @@ -1,189 +0,0 @@ -# MariaDB database server configuration file. -# -# You can copy this file to one of: -# - "/etc/mysql/my.cnf" to set global options, -# - "~/.my.cnf" to set user-specific options. -# -# One can use all long options that the program supports. -# Run program with --help to get a list of available options and with -# --print-defaults to see which it would actually understand and use. -# -# For explanations see -# http://dev.mysql.com/doc/mysql/en/server-system-variables.html - -# This will be passed to all mysql clients -# It has been reported that passwords should be enclosed with ticks/quotes -# escpecially if they contain "#" chars... -# Remember to edit /etc/mysql/debian.cnf when changing the socket location. -[client] -port = 3306 -socket = /var/run/mysqld/mysqld.sock - -# Here is entries for some specific programs -# The following values assume you have at least 32M ram - -# This was formally known as [safe_mysqld]. Both versions are currently parsed. -[mysqld_safe] -socket = /var/run/mysqld/mysqld.sock -nice = 0 - -[mysqld] -# -# * Basic Settings -# -user = mysql -pid-file = /var/run/mysqld/mysqld.pid -socket = /var/run/mysqld/mysqld.sock -port = 3306 -basedir = /usr -datadir = /var/lib/mysql -tmpdir = /tmp -lc_messages_dir = /usr/share/mysql -lc_messages = en_US -skip-external-locking -# -# Instead of skip-networking the default is now to listen only on -# localhost which is more compatible and is not less secure. -bind-address = 0.0.0.0 -# -# * Fine Tuning -# -max_connections = 100 -connect_timeout = 5 -wait_timeout = 600 -max_allowed_packet = 16M -thread_cache_size = 128 -sort_buffer_size = 4M -bulk_insert_buffer_size = 16M -tmp_table_size = 32M -max_heap_table_size = 32M -# -# * MyISAM -# -# This replaces the startup script and checks MyISAM tables if needed -# the first time they are touched. On error, make copy and try a repair. -myisam_recover_options = BACKUP -key_buffer_size = 128M -#open-files-limit = 2000 -table_open_cache = 400 -myisam_sort_buffer_size = 512M -concurrent_insert = 2 -read_buffer_size = 2M -read_rnd_buffer_size = 1M -# -# * Query Cache Configuration -# -# Cache only tiny result sets, so we can fit more in the query cache. -query_cache_limit = 128K -query_cache_size = 64M -# for more write intensive setups, set to DEMAND or OFF -#query_cache_type = DEMAND -# -# * Logging and Replication -# -# Both location gets rotated by the cronjob. -# Be aware that this log type is a performance killer. -# As of 5.1 you can enable the log at runtime! -#general_log_file = /var/log/mysql/mysql.log -#general_log = 1 -# -# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf. -# -# we do want to know about network errors and such -log_warnings = 2 -# -# Enable the slow query log to see queries with especially long duration -#slow_query_log[={0|1}] -slow_query_log_file = /var/log/mysql/mariadb-slow.log -long_query_time = 10 -#log_slow_rate_limit = 1000 -log_slow_verbosity = query_plan - -#log-queries-not-using-indexes -#log_slow_admin_statements -# -# The following can be used as easy to replay backup logs or for replication. -# note: if you are setting up a replication slave, see README.Debian about -# other settings you may need to change. -#server-id = 1 -#report_host = master1 -#auto_increment_increment = 2 -#auto_increment_offset = 1 -log_bin = /var/log/mysql/mariadb-bin -log_bin_index = /var/log/mysql/mariadb-bin.index -# not fab for performance, but safer -#sync_binlog = 1 -expire_logs_days = 10 -max_binlog_size = 100M -# slaves -#relay_log = /var/log/mysql/relay-bin -#relay_log_index = /var/log/mysql/relay-bin.index -#relay_log_info_file = /var/log/mysql/relay-bin.info -#log_slave_updates -#read_only -# -# If applications support it, this stricter sql_mode prevents some -# mistakes like inserting invalid dates etc. -#sql_mode = NO_ENGINE_SUBSTITUTION,TRADITIONAL -# -# * InnoDB -# -# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. -# Read the manual for more InnoDB related options. There are many! -default_storage_engine = InnoDB -# you can't just change log file size, requires special procedure -#innodb_log_file_size = 50M -innodb_buffer_pool_size = 256M -innodb_log_buffer_size = 8M -innodb_file_per_table = 1 -innodb_open_files = 400 -innodb_io_capacity = 400 -innodb_flush_method = O_DIRECT -# -# * Security Features -# -# Read the manual, too, if you want chroot! -# chroot = /var/lib/mysql/ -# -# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". -# -# ssl-ca=/etc/mysql/cacert.pem -# ssl-cert=/etc/mysql/server-cert.pem -# ssl-key=/etc/mysql/server-key.pem - -# -# * Galera-related settings -# -[galera] -# Mandatory settings -#wsrep_on=ON -#wsrep_provider= -#wsrep_cluster_address= -#binlog_format=row -#default_storage_engine=InnoDB -#innodb_autoinc_lock_mode=2 -# -# Allow server to accept connections on all interfaces. -# -#bind-address=0.0.0.0 -# -# Optional setting -#wsrep_slave_threads=1 -#innodb_flush_log_at_trx_commit=0 - -[mysqldump] -quick -quote-names -max_allowed_packet = 16M - -[mysql] -#no-auto-rehash # faster start of mysql but no tab completion - -[isamchk] -key_buffer = 16M - -# -# * IMPORTANT: Additional settings that can override those from this file! -# The files must end with '.cnf', otherwise they'll be ignored. -# -!includedir /etc/mysql/conf.d/ diff --git a/.dev/vagrant/motd.txt b/.dev/vagrant/motd.txt deleted file mode 100644 index 22089d55a..000000000 --- a/.dev/vagrant/motd.txt +++ /dev/null @@ -1,17 +0,0 @@ -##################################################### - Pterodactyl Panel Vagrant VM - -Install: /var/www/html/pterodactyl -Ports: - Panel: 80 (50080 on host) - MailHog: 8025 (58025 on host) - MySQL: 3306 (53306 on host) - -Default panel users: - user: admin passwd: Ptero123 (admin user) - user: user passwd: Ptero123 (standard user) - -MySQL is accessible using root/pterodactyl or pterodactyl/pterodactyl - -Service for pteroq and mailhog are running -##################################################### diff --git a/.dev/vagrant/provision.sh b/.dev/vagrant/provision.sh deleted file mode 100644 index 38dc27ad8..000000000 --- a/.dev/vagrant/provision.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -echo "Provisioning development environment for Pterodactyl Panel." -cp /var/www/html/pterodactyl/.dev/vagrant/motd.txt /etc/motd -chmod -x /etc/update-motd.d/10-help-text /etc/update-motd.d/51-cloudguest - -apt-get install -y software-properties-common > /dev/null - -echo "Add the ondrej/php ppa repository" -add-apt-repository -y ppa:ondrej/php > /dev/null -echo "Add the mariadb repository" -curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash > /dev/null - -apt-get update > /dev/null - -echo "Install the dependencies" -export DEBIAN_FRONTEND=noninteractive -# set the mariadb root password because mariadb asks for it -debconf-set-selections <<< 'mariadb-server-5.5 mysql-server/root_password password pterodactyl' -debconf-set-selections <<< 'mariadb-server-5.5 mysql-server/root_password_again password pterodactyl' -# actually install -apt-get install -y php7.2 php7.2-cli php7.2-gd php7.2-mysql php7.2-pdo php7.2-mbstring php7.2-tokenizer php7.2-bcmath php7.2-xml php7.2-fpm php7.2-memcached php7.2-curl php7.2-zip php-xdebug mariadb-server nginx curl tar unzip git memcached > /dev/null - -echo "Install composer" -curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer - -echo "Install and run mailhog" -curl -sL -o /usr/bin/mailhog https://github.com/mailhog/MailHog/releases/download/v1.0.0/MailHog_linux_amd64 -chmod +x /usr/bin/mailhog -cp /var/www/html/pterodactyl/.dev/vagrant/mailhog.service /etc/systemd/system/ -systemctl enable mailhog.service -systemctl start mailhog - -echo "Configure xDebug" -cp /var/www/html/pterodactyl/.dev/vagrant/xdebug.ini /etc/php/7.2/mods-available/ -systemctl restart php7.2-fpm - -echo "Configure nginx" -cp /var/www/html/pterodactyl/.dev/vagrant/pterodactyl.conf /etc/nginx/sites-available/ -rm /etc/nginx/sites-available/default -ln -s /etc/nginx/sites-available/pterodactyl.conf /etc/nginx/sites-enabled/pterodactyl.conf -systemctl restart nginx - -echo "Setup database" -# Replace default config with custom one to bind mysql to 0.0.0.0 to make it accessible from the host -cp /var/www/html/pterodactyl/.dev/vagrant/mariadb.cnf /etc/mysql/my.cnf -systemctl restart mariadb -mysql -u root -ppterodactyl << SQL -CREATE DATABASE panel; -GRANT ALL PRIVILEGES ON panel.* TO 'pterodactyl'@'%' IDENTIFIED BY 'pterodactyl' WITH GRANT OPTION; -GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'pterodactyl' WITH GRANT OPTION; -FLUSH PRIVILEGES; -SQL - -echo "Setup pterodactyl queue worker service" -cp /var/www/html/pterodactyl/.dev/vagrant/pteroq.service /etc/systemd/system/ -systemctl enable pteroq.service - - -echo "Setup panel with base settings" -cp /var/www/html/pterodactyl/.dev/vagrant/.env.vagrant /var/www/html/pterodactyl/.env -cd /var/www/html/pterodactyl -chmod -R 755 storage/* bootstrap/cache -composer install --no-progress -php artisan key:generate --force -php artisan migrate -php artisan db:seed -php artisan p:user:make --name-first Test --name-last Admin --username admin --email testadmin@pterodactyl.io --password Ptero123 --admin 1 -php artisan p:user:make --name-first Test --name-last User --username user --email testuser@pterodactyl.io --password Ptero123 --admin 0 - -echo "Add queue cronjob and start queue worker" -(crontab -l 2>/dev/null; echo "* * * * * php /var/www/html/pterodactyl/artisan schedule:run >> /dev/null 2>&1") | crontab - -systemctl start pteroq - -echo " ----------------" -echo "Provisioning is completed." -echo "The panel should be available at http://localhost:50080/" -echo "You may use the default admin user to login: admin/Ptero123" -echo "A normal user has also been created: user/Ptero123" -echo "MailHog is available at http://localhost:58025/" -echo "Connect to the database using root/pterodactyl or pterodactyl/pterodactyl on localhost:53306" -echo "If you want to access the panel using http://pterodactyl.app you can use the vagrant-dns plugin" -echo "Install it with 'vagrant plugin install vagrant-dns', then run 'vagrant dns --install' once" -echo "On first use you'll have to manually start vagrant-dns with 'vagrant dns --start'" diff --git a/.dev/vagrant/pterodactyl.conf b/.dev/vagrant/pterodactyl.conf deleted file mode 100644 index 343cbad5f..000000000 --- a/.dev/vagrant/pterodactyl.conf +++ /dev/null @@ -1,51 +0,0 @@ -# If using Ubuntu this file should be placed in: -# /etc/nginx/sites-available/ -# -# If using CentOS this file should be placed in: -# /etc/nginx/conf.d/ -# -server { - listen 80; - server_name 0.0.0.0; - - root /var/www/html/pterodactyl/public; - index index.html index.htm index.php; - charset utf-8; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location = /favicon.ico { access_log off; log_not_found off; } - location = /robots.txt { access_log off; log_not_found off; } - - access_log off; - error_log /var/log/nginx/pterodactyl.app-error.log error; - - # allow larger file uploads and longer script runtimes - client_max_body_size 100m; - client_body_timeout 120s; - - sendfile off; - - location ~ \.php$ { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - # the fastcgi_pass path needs to be changed accordingly when using CentOS - fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTP_PROXY ""; - fastcgi_intercept_errors off; - fastcgi_buffer_size 16k; - fastcgi_buffers 4 16k; - fastcgi_connect_timeout 300; - fastcgi_send_timeout 300; - fastcgi_read_timeout 300; - } - - location ~ /\.ht { - deny all; - } -} diff --git a/.dev/vagrant/pteroq.service b/.dev/vagrant/pteroq.service deleted file mode 100644 index 7828ee91b..000000000 --- a/.dev/vagrant/pteroq.service +++ /dev/null @@ -1,20 +0,0 @@ -# Pterodactyl Queue Worker File -# ---------------------------------- -# File should be placed in: -# /etc/systemd/system -# -# nano /etc/systemd/system/pteroq.service - -[Unit] -Description=Pterodactyl Queue Worker - -[Service] -# On some systems the user and group might be different. -# Some systems use `apache` as the user and group. -User=www-data -Group=www-data -Restart=on-failure -ExecStart=/usr/bin/php /var/www/html/pterodactyl/artisan queue:work database --queue=high,standard,low --sleep=3 --tries=3 - -[Install] -WantedBy=multi-user.target diff --git a/.dev/vagrant/xdebug.ini b/.dev/vagrant/xdebug.ini deleted file mode 100644 index 1725b8e84..000000000 --- a/.dev/vagrant/xdebug.ini +++ /dev/null @@ -1,10 +0,0 @@ -zend_extension=xdebug.so - -xdebug.remote_enable=1 -xdebug.remote_connect_back=1 -xdebug.remote_port=9000 -xdebug.scream=0 -xdebug.show_local_vars=1 -xdebug.idekey=PHPSTORM - -xdebug.remote_log=/tmp/xdebug.log \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index bc49d523e..a04147116 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,12 +1,17 @@ root = true [*] -end_of_line = lf -insert_final_newline = true indent_style = space indent_size = 4 +tab_width = 4 +end_of_line = lf charset = utf-8 trim_trailing_whitespace = true +insert_final_newline = true [*.md] trim_trailing_whitespace = false + +[*.{md,nix,yml,yaml}] +indent_size = 2 +tab_width = 2 diff --git a/.env.example b/.env.example index 0b59de0c9..eea0a9d03 100644 --- a/.env.example +++ b/.env.example @@ -1,31 +1,43 @@ APP_ENV=production APP_DEBUG=false APP_KEY= -APP_THEME=pterodactyl -APP_TIMEZONE=America/New_York -APP_CLEAR_TASKLOG=720 -APP_DELETE_MINUTES=10 -APP_ENVIRONMENT_ONLY=true -LOG_CHANNEL=daily +APP_TIMEZONE=UTC +APP_URL=http://panel.example.com APP_LOCALE=en +APP_ENVIRONMENT_ONLY=true +LOG_CHANNEL=daily +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=panel DB_USERNAME=pterodactyl DB_PASSWORD= +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +CACHE_DRIVER=file +QUEUE_CONNECTION=redis +SESSION_DRIVER=file + HASHIDS_SALT= HASHIDS_LENGTH=8 -MAIL_DRIVER=smtp -MAIL_HOST=mailtrap.io -MAIL_PORT=2525 +MAIL_MAILER=smtp +MAIL_HOST=smtp.example.com +MAIL_PORT=25 MAIL_USERNAME= MAIL_PASSWORD= MAIL_ENCRYPTION=tls -MAIL_FROM=no-reply@example.com - -QUEUE_HIGH=high -QUEUE_STANDARD=standard -QUEUE_LOW=low \ No newline at end of file +MAIL_FROM_ADDRESS=no-reply@example.com +MAIL_FROM_NAME="Pterodactyl Panel" +# You should set this to your domain to prevent it defaulting to 'localhost', causing +# mail servers such as Gmail to reject your mail. +# +# @see: https://github.com/pterodactyl/panel/pull/3110 +# MAIL_EHLO_DOMAIN=panel.example.com diff --git a/.env.travis b/.env.travis deleted file mode 100644 index e0040b948..000000000 --- a/.env.travis +++ /dev/null @@ -1,19 +0,0 @@ -APP_ENV=testing -APP_DEBUG=true -APP_KEY=SomeRandomString3232RandomString -APP_THEME=pterodactyl -APP_TIMEZONE=UTC -APP_URL=http://localhost/ - -TESTING_DB_HOST=127.0.0.1 -TESTING_DB_DATABASE=travis -TESTING_DB_USERNAME=root -TESTING_DB_PASSWORD="" - -CACHE_DRIVER=array -SESSION_DRIVER=array -MAIL_DRIVER=array -QUEUE_DRIVER=sync - -HASHIDS_SALT=test123 -APP_ENVIRONMENT_ONLY=true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..01050140a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +public +node_modules +resources/views +babel.config.js +tailwind.config.js +webpack.config.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..8f37e1cf2 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,52 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + project: './tsconfig.json', + tsconfigRootDir: './', + }, + settings: { + react: { + pragma: 'React', + version: 'detect', + }, + linkComponents: [ + { name: 'Link', linkAttribute: 'to' }, + { name: 'NavLink', linkAttribute: 'to' }, + ], + }, + env: { + browser: true, + es6: true, + }, + plugins: ['react', 'react-hooks', 'prettier', '@typescript-eslint'], + extends: [ + // 'standard', + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:@typescript-eslint/recommended', + ], + rules: { + eqeqeq: 'error', + 'prettier/prettier': ['error', {}, { usePrettierrc: true }], + // TypeScript can infer this significantly better than eslint ever can. + 'react/prop-types': 0, + 'react/display-name': 0, + 'react/no-unknown-property': ['error', {ignore: ['css']}], + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-non-null-assertion': 0, + // This setup is required to avoid a spam of errors when running eslint about React being + // used before it is defined. + // + // @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md#how-to-use + 'no-use-before-define': 0, + '@typescript-eslint/no-use-before-define': 'warn', + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + '@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }], + }, +}; diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..a21d39174 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [matthewpi] diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md deleted file mode 100644 index 640d6def5..000000000 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: "\U0001F41B Bug Report" -about: For reporting code or design bugs with the software. DO NOT REPORT APACHE/NGINX/PHP CONFIGURATION ISSUES. - ---- - -DO NOT REPORT ISSUES CONFIGURING: SSL, PHP, APACHE, NGINX, YOUR MACHINE, SSH, SFTP, ETC. ON THIS GITHUB TRACKER. - -For assistance installating this software, as well as debugging issues with dependencies, please use our discord server: https://discord.gg/pterodactyl - -You MUST complete all of the below information when reporting a bug, failure to do so will result in closure of your issue. PLEASE stop spamming our tracker with "bugs" that are not related to this project. - -**STOP: READ FIRST, AND THEN DELETE THE ABOVE LINES** - -**Background (please complete the following information):** -* Panel or Daemon: -* Version of Panel/Daemon: -* Server's OS: -* Your Computer's OS & Browser: - -**Describe the bug** -A clear and concise description of what the bug is. -Please provide additional information too, depending on what you have issues with: -Panel: `php -v` (the php version in use). -Daemon: `uname -a` and `docker info` (your kernel version and information regarding docker) - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. If applicable, add screenshots or a recording to help explain your problem. diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md deleted file mode 100644 index 64fa679ba..000000000 --- a/.github/ISSUE_TEMPLATE/---feature-request.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: "\U0001F680 Feature Request" -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/--installation-help.md b/.github/ISSUE_TEMPLATE/--installation-help.md deleted file mode 100644 index 4d5d27e71..000000000 --- a/.github/ISSUE_TEMPLATE/--installation-help.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: "⛔ Installation Help" -about: 'Visit our Discord for installation help: https://pterodactyl.io/discord' - ---- - -We use GitHub issues only to discuss about Pterodactyl bugs and new features. For -this kind of questions about using Pterodactyl, please visit our Discord for assistance: https://pterodactyl.io/discord - -DO NOT REPORT ISSUES CONFIGURING: SSL, PHP, APACHE, NGINX, YOUR MACHINE, SSH, SFTP, ETC. ON THIS GITHUB TRACKER. - -For assistance installating this software, as well as debugging issues with dependencies, please use our discord server: https://discord.gg/pterodactyl - -PLEASE stop spamming our tracker with "bugs" that are not related to this project. diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..c7bc63aad --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,87 @@ +name: Bug Report +description: Something isn't working quite right in the software. +labels: [not confirmed] +body: +- type: markdown + attributes: + value: | + Bug reports should only be used for reporting issues with how the software works. For assistance installing this software, as well as debugging issues with dependencies, please use our [Discord server](https://discord.gg/pterodactyl). + +- type: textarea + attributes: + label: Current Behavior + description: Please provide a clear & concise description of the issue. + validations: + required: true + +- type: textarea + attributes: + label: Expected Behavior + description: Please describe what you expected to happen. + validations: + required: true + +- type: textarea + attributes: + label: Steps to Reproduce + description: Please be as detailed as possible when providing steps to reproduce, failure to provide steps will result in this issue being closed. + validations: + required: true + +- type: input + id: panel-version + attributes: + label: Panel Version + description: Version number of your Panel (latest is not a version) + placeholder: 1.4.0 + validations: + required: true + +- type: input + id: wings-version + attributes: + label: Wings Version + description: Version number of your Wings (latest is not a version) + placeholder: 1.4.2 + validations: + required: true + +- type: input + id: egg-details + attributes: + label: Games and/or Eggs Affected + description: Please include the specific game(s) or egg(s) you are running into this bug with. + placeholder: Minecraft (Paper), Minecraft (Forge) + +- type: input + id: docker-image + attributes: + label: Docker Image + description: The specific Docker image you are using for the game(s) above. + placeholder: ghcr.io/pterodactyl/yolks:java_17 + +- type: textarea + id: panel-logs + attributes: + label: Error Logs + description: | + Run the following command to collect logs on your system. + + Wings: `sudo wings diagnostics` + Panel: `tail -n 150 /var/www/pterodactyl/storage/logs/laravel-$(date +%F).log | nc pteropaste.com 99` + placeholder: "https://pteropaste.com/a1h6z" + render: bash + validations: + required: false + +- type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please [search here](https://github.com/pterodactyl/panel/issues) to see if an issue already exists for your problem. + options: + - label: I have searched the existing issues before opening this issue. + required: true + - label: I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server. + required: true + - label: I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..fee5ad948 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Installation Help + url: https://discord.gg/pterodactyl + about: Please visit our Discord for help with your installation. + - name: General Question + url: https://discord.gg/pterodactyl + about: Please visit our Discord for general questions about Pterodactyl. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 000000000..d782e3983 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,32 @@ +name: Feature Request +description: Suggest a new feature or improvement for the software. +labels: [feature request] +body: +- type: checkboxes + attributes: + label: Is there an existing feature request for this? + description: Please [search here](https://github.com/pterodactyl/panel/issues?q=is%3Aissue) to see if someone else has already suggested this. + options: + - label: I have searched the existing issues before opening this feature request. + required: true + +- type: textarea + attributes: + label: Describe the feature you would like to see. + description: "A clear & concise description of the feature you'd like to have added, and what issues it would solve." + validations: + required: true + +- type: textarea + attributes: + label: Describe the solution you'd like. + description: "You must explain how you'd like to see this feature implemented. Technical implementation details are not necessary, rather an idea of how you'd like to see this feature used." + validations: + required: true + +- type: textarea + attributes: + label: Additional context to this request. + description: "Add any other context or screenshots about the feature request." + validations: + required: false diff --git a/.github/docker/Caddyfile b/.github/docker/Caddyfile new file mode 100644 index 000000000..9d16e2982 --- /dev/null +++ b/.github/docker/Caddyfile @@ -0,0 +1,21 @@ +:8080 { + root * /var/www/pterodactyl/public/ + file_server + + header { + -Server + -X-Powered-By + Referrer-Policy "same-origin" + X-Frame-Options "deny" + X-XSS-Protection "1; mode=block" + X-Content-Type-Options "nosniff" + } + + encode gzip zstd + + php_fastcgi 127.0.0.1:9000 { + trusted_proxies 172.20.0.0/16 + } + + try_files {path} {path}/ /index.php?{query} +} diff --git a/.github/docker/php-fpm.conf b/.github/docker/php-fpm.conf new file mode 100644 index 000000000..9812e6610 --- /dev/null +++ b/.github/docker/php-fpm.conf @@ -0,0 +1,21 @@ +[global] +error_log = /dev/stderr +daemonize = no + +[www] +user = nobody +group = nobody + +listen = 127.0.0.1:9000 + +pm = dynamic +pm.start_servers = 4 +pm.min_spare_servers = 4 +pm.max_spare_servers = 16 +pm.max_children = 64 +pm.max_requests = 256 + +clear_env = no +catch_workers_output = yes + +decorate_workers_output = no diff --git a/.github/docker/supervisord.conf b/.github/docker/supervisord.conf new file mode 100644 index 000000000..be659165b --- /dev/null +++ b/.github/docker/supervisord.conf @@ -0,0 +1,57 @@ +[supervisord] +logfile=/dev/stdout +logfile_maxbytes=0 +loglevel=info +minfds=1024 +minprocs=200 +nodaemon=true +pidfile=/dev/null + +[unix_http_server] +file=/tmp/supervisor.sock + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[program:caddy] +command=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile +autostart=true +autorestart=true +priority=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:php-fpm] +command=/usr/sbin/php-fpm --nodaemonize +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +environment=LOG_CHANNEL="stderr" + +[program:queue-worker] +command=/usr/bin/php /var/www/pterodactyl/artisan queue:work --queue=standard --sleep=3 --tries=3 +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +environment=LOG_CHANNEL="stderr" + +[program:yacron] +command=/usr/local/bin/yacron -c /etc/yacron.yaml +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +environment=LOG_CHANNEL="stderr" diff --git a/.github/docker/yacron.yaml b/.github/docker/yacron.yaml new file mode 100644 index 000000000..c0dc75ecd --- /dev/null +++ b/.github/docker/yacron.yaml @@ -0,0 +1,8 @@ +jobs: + - name: scheduler + command: + - /usr/bin/php + - /var/www/pterodactyl/artisan + - schedule:run + schedule: "* * * * *" + utc: true diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 000000000..d08c847a7 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,67 @@ +name: Docker + +on: + push: + branches: + - develop + - 1.0-develop + pull_request: + branches: + - develop + - 1.0-develop + release: + types: + - published + +jobs: + push: + name: Push + runs-on: ubuntu-22.04 + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Fetch metadata + id: metadata + uses: docker/metadata-action@v4 + with: + images: ghcr.io/pterodactyl/panel + flavor: | + latest=false + tags: | + type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.action == 'published' && github.event.release.prerelease == false }} + type=ref,event=tag + type=ref,event=branch + + - name: Setup QEMU + uses: docker/setup-qemu-action@v2 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + if: "github.event_name != 'pull_request'" + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Update version + if: "github.event_name == 'release' && github.event.action == 'published'" + env: + REF: ${{ github.event.release.tag_name }} + run: | + sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:1}',/" config/app.php + + - name: Build and Push + uses: docker/build-push-action@v4 + with: + context: . + file: ./Containerfile + push: ${{ github.event_name != 'pull_request' }} + platforms: linux/amd64,linux/arm64 + labels: ${{ steps.metadata.outputs.labels }} + tags: ${{ steps.metadata.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/laravel.yaml b/.github/workflows/laravel.yaml new file mode 100644 index 000000000..3e75e8839 --- /dev/null +++ b/.github/workflows/laravel.yaml @@ -0,0 +1,210 @@ +name: Laravel + +on: + push: + branches: + - "develop" + - "1.0-develop" + pull_request: + branches: + - "develop" + - "1.0-develop" + +jobs: + analysis: + name: Static Analysis + runs-on: ubuntu-22.04 + env: + APP_ENV: testing + APP_DEBUG: "true" + APP_KEY: SomeRandomString3232RandomString + CACHE_DRIVER: array + MAIL_MAILER: array + SESSION_DRIVER: array + QUEUE_CONNECTION: sync + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --no-progress --no-suggest --prefer-dist + + - name: Analyze + run: vendor/bin/phpstan analyse + + lint: + name: Lint + runs-on: ubuntu-22.04 + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + # TODO: Update to 8.2 once php-cs-fixer supports it + php-version: 8.1 + extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --no-progress --no-suggest --prefer-dist + + - name: PHP CS Fixer + run: vendor/bin/php-cs-fixer fix --dry-run --diff + + mysql: + name: Tests + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + php: [8.1, 8.2] + database: ["mariadb:10.2", "mariadb:10.9", "mysql:8"] + services: + database: + image: docker.io/library/${{ matrix.database }} + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: testing + ports: + - 3306/tcp + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + env: + APP_ENV: testing + APP_DEBUG: "true" + APP_KEY: SomeRandomString3232RandomString + APP_THEME: pterodactyl + APP_TIMEZONE: UTC + APP_URL: http://localhost/ + APP_ENVIRONMENT_ONLY: "true" + CACHE_DRIVER: array + MAIL_MAILER: array + SESSION_DRIVER: array + QUEUE_CONNECTION: sync + HASHIDS_SALT: test123 + DB_CONNECTION: mysql + DB_HOST: 127.0.0.1 + DB_DATABASE: testing + DB_USERNAME: root + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Get cache directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.php }}- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --no-progress --no-suggest --prefer-dist + + - name: Unit tests + run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit + env: + DB_HOST: UNIT_NO_DB + + - name: Integration tests + run: vendor/bin/phpunit tests/Integration + env: + DB_PORT: ${{ job.services.database.ports[3306] }} + + postgres: + name: Tests + runs-on: ubuntu-22.04 + if: "!contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + php: [8.1, 8.2] + database: ["postgres:13", "postgres:14", "postgres:15"] + services: + database: + image: docker.io/library/${{ matrix.database }} + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: testing + ports: + - 5432/tcp + options: --health-cmd=pg_isready --health-interval=10s --health-timeout=5s --health-retries=3 + env: + APP_ENV: testing + APP_DEBUG: "true" + APP_KEY: SomeRandomString3232RandomString + APP_THEME: pterodactyl + APP_TIMEZONE: UTC + APP_URL: http://localhost/ + APP_ENVIRONMENT_ONLY: "true" + CACHE_DRIVER: array + MAIL_MAILER: array + SESSION_DRIVER: array + QUEUE_CONNECTION: sync + HASHIDS_SALT: test123 + DB_CONNECTION: pgsql + DB_HOST: 127.0.0.1 + DB_DATABASE: testing + DB_USERNAME: postgres + DB_PASSWORD: postgres + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Get cache directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.php }}- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip + tools: composer:v2 + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --no-progress --no-suggest --prefer-dist + + - name: Unit tests + run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit + env: + DB_HOST: UNIT_NO_DB + + - name: Integration tests + run: vendor/bin/phpunit tests/Integration + env: + DB_PORT: ${{ job.services.database.ports[5432] }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 000000000..f931f6abf --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,92 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + release: + name: Release + runs-on: ubuntu-22.04 + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: pnpm + + - name: Install dependencies + run: pnpm install + + - name: Build + run: pnpm run build + + - name: Create release branch and bump version + env: + REF: ${{ github.ref }} + run: | + BRANCH=release/${REF:10} + git config --local user.email "ci@pterodactyl.io" + git config --local user.name "Pterodactyl CI" + git checkout -b $BRANCH + git push -u origin $BRANCH + sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:11}',/" config/app.php + git add config/app.php + git commit -m "ci(release): bump version" + git push + + - name: Create release archive + run: | + rm -rf node_modules tests CODE_OF_CONDUCT.md CONTRIBUTING.md flake.lock flake.nix phpstan.neon phpunit.xml shell.nix + tar -czf panel.tar.gz * .editorconfig .env.example .eslintignore .eslintrc.js .gitignore .prettierrc.json + + - name: Extract changelog + env: + REF: ${{ github.ref }} + run: | + sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG + + - name: Create checksum and add to changelog + run: | + SUM=`sha256sum panel.tar.gz` + echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG + echo $SUM > checksum.txt + + - name: Create release + id: create_release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + draft: true + prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} + body_path: ./RELEASE_CHANGELOG + + - name: Upload release archive + id: upload-release-archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: panel.tar.gz + asset_name: panel.tar.gz + asset_content_type: application/gzip + + - name: Upload release checksum + id: upload-release-checksum + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./checksum.txt + asset_name: checksum.txt + asset_content_type: text/plain diff --git a/.github/workflows/ui.yaml b/.github/workflows/ui.yaml new file mode 100644 index 000000000..24c874d5e --- /dev/null +++ b/.github/workflows/ui.yaml @@ -0,0 +1,63 @@ +name: UI + +on: + push: + branches: + - "develop" + - "1.0-develop" + pull_request: + branches: + - "develop" + - "1.0-develop" + +jobs: + lint: + name: Lint + runs-on: ubuntu-22.04 + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: pnpm + + - name: Install dependencies + run: pnpm install + + - name: Lint + run: pnpm run lint + + tests: + name: Tests + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + node: [16, 18] + steps: + - name: Code checkout + uses: actions/checkout@v3 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + cache: pnpm + + - name: Install dependencies + run: pnpm install + + - name: Build + run: pnpm run build + + - name: Tests + run: pnpm run test diff --git a/.gitignore b/.gitignore index 9fc4aee54..2c9fda4ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,36 @@ /vendor *.DS_Store* -.env +!.env.ci +!.env.example +.env* .vagrant/* .vscode/* storage/framework/* /.idea /nbproject +/.direnv -package-lock.json -composer.lock node_modules - -_ide_helper_models.php +*.log _ide_helper.php - -sami.phar -/.sami +_ide_helper_models.php +.phpstorm.meta.php +.yarn +public/assets/manifest.json # For local development with docker # Remove if we ever put the Dockerfile in the repo .dockerignore -#Dockerfile docker-compose.yml # for image related files misc -.phpstorm.meta.php -.php_cs.cache - +.php-cs-fixer.cache coverage.xml +resources/lang/locales.js +.phpunit.result.cache -# Vagrant -*.log +/public/build +/public/hot +result +docker-compose.yaml diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..bf2e7648b --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=true diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 000000000..9245b7e6e --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,52 @@ +in(__DIR__) + ->exclude([ + 'vendor', + 'node_modules', + 'storage', + 'bootstrap/cache', + ]) + ->notName(['_ide_helper*']); + +return (new Config()) + ->setRiskyAllowed(true) + ->setFinder($finder) + ->setRules([ + '@Symfony' => true, + '@PSR1' => true, + '@PSR2' => true, + '@PSR12' => true, + 'align_multiline_comment' => ['comment_type' => 'phpdocs_like'], + 'combine_consecutive_unsets' => true, + 'concat_space' => ['spacing' => 'one'], + 'heredoc_to_nowdoc' => true, + 'no_alias_functions' => true, + 'no_unreachable_default_argument_value' => true, + 'no_useless_return' => true, + 'ordered_imports' => [ + 'sort_algorithm' => 'length', + ], + 'phpdoc_align' => [ + 'align' => 'left', + 'tags' => [ + 'param', + 'property', + 'return', + 'throws', + 'type', + 'var', + ], + ], + 'random_api_migration' => true, + 'ternary_to_null_coalescing' => true, + 'yoda_style' => [ + 'equal' => false, + 'identical' => false, + 'less_and_greater' => false, + ], + ]); diff --git a/.php_cs b/.php_cs deleted file mode 100644 index c854af47c..000000000 --- a/.php_cs +++ /dev/null @@ -1,55 +0,0 @@ -in([ - 'app', - 'bootstrap', - 'config', - 'database', - 'resources/lang', - 'routes', - 'tests', - ]); - -return PhpCsFixer\Config::create() - ->setRules([ - '@Symfony' => true, - '@PSR1' => true, - '@PSR2' => true, - 'align_multiline_comment' => ['comment_type' => 'phpdocs_like'], - 'array_syntax' => ['syntax' => 'short'], - 'blank_line_before_return' => true, - 'blank_line_before_statement' => false, - 'combine_consecutive_unsets' => true, - 'concat_space' => ['spacing' => 'one'], - 'declare_equal_normalize' => ['space' => 'single'], - 'heredoc_to_nowdoc' => true, - 'increment_style' => ['style' => 'post'], - 'linebreak_after_opening_tag' => true, - 'method_argument_space' => [ - 'ensure_fully_multiline' => false, - 'keep_multiple_spaces_after_comma' => false, - ], - 'new_with_braces' => false, - 'no_alias_functions' => true, - 'no_multiline_whitespace_before_semicolons' => true, - 'no_unreachable_default_argument_value' => true, - 'no_useless_return' => true, - 'not_operator_with_successor_space' => true, - 'ordered_imports' => [ - 'sortAlgorithm' => 'length', - ], - 'phpdoc_align' => ['tags' => ['param']], - 'phpdoc_separation' => false, - 'protected_to_private' => false, - 'psr0' => ['dir' => 'app'], - 'psr4' => true, - 'random_api_migration' => true, - 'standardize_not_equals' => true, - 'ternary_to_null_coalescing' => true, - 'yoda_style' => [ - 'equal' => false, - 'identical' => false, - 'less_and_greater' => false, - ], - ])->setRiskyAllowed(true)->setFinder($finder); diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..e5eee8ad4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.github +public +node_modules +resources/views diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..296147a80 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,22 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "endOfLine": "lf", + "overrides": [ + { + "files": ["**/*.md"], + "options": { + "tabWidth": 2, + "useTabs": false, + "singleQuote": false + } + } + ] +} diff --git a/.sami.php b/.sami.php deleted file mode 100644 index da4c4658f..000000000 --- a/.sami.php +++ /dev/null @@ -1,16 +0,0 @@ -files() - ->name('*.php') - ->in($dir = __DIR__ . '/app'); - -return new Sami($iterator, array( - 'title' => 'Pterodactyl', - 'build_dir' => __DIR__ . '/.sami/build', - 'cache_dir' => __DIR__ . '/.sami/cache', - 'default_opened_level' => 2, -)); diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 87848d020..000000000 --- a/.styleci.yml +++ /dev/null @@ -1,7 +0,0 @@ -preset: laravel -risky: false -disabled: - - concat_without_spaces -enabled: - - concat_with_spaces - - no_unused_imports diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ff915fa1e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,44 +0,0 @@ -language: php -dist: trusty -git: - depth: 3 - quiet: true -matrix: - fast_finish: true - allow_failures: - - env: TEST_SUITE=Coverage -env: - matrix: - - TEST_SUITE=Unit - - TEST_SUITE=Coverage - - TEST_SUITE=Integration -php: - - 7.2 -sudo: false -cache: - directories: - - $HOME/.composer/cache -services: - - mysql -before_install: - - mysql -e 'CREATE DATABASE IF NOT EXISTS travis;' -before_script: - - echo 'opcache.enable_cli=1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - - cp .env.travis .env - - travis_retry composer install --no-interaction --prefer-dist --no-suggest -script: - - if [ "$TEST_SUITE" = "Unit" ]; then vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit; fi; - - if [ "$TEST_SUITE" = "Coverage" ]; then vendor/bin/phpunit --bootstrap vendor/autoload.php --coverage-clover coverage.xml tests/Unit; fi; - - if [ "$TEST_SUITE" = "Integration" ]; then vendor/bin/phpunit tests/Integration; fi; -notifications: - email: false - webhooks: - urls: - - https://misc.schrej.net/travistodiscord/pterodev.php - on_success: change - on_failure: always - on_error: always - on_cancel: always - on_start: never -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36586d886..938a00397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,654 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v1.11.3 +### Changed +* When updating a server's description through the client API, if no value is specified, the description will now remain unchanged. +* When installing the Panel for the first time, the queue driver will now all default to `redis` instead of `sync`. + +### Fixed +* `php artisan p:environment:mail` not correctly setting the right variable for `MAIL_FROM_ADDRESS`. +* Fixed the conflict state rendering on the UI for a server showing `reinstall_failed` as `restoring_backup`. +* Fixed the unknown column `uuid` error when jobs fail, causing them not to get stored correctly. +* Fixed the server task endpoints in the client API not allowing `sequence_id` and `continue_on_failure` to be set. + +## v1.11.2 +### Changed +* Telemetry no longer sends a map of Egg and Nest UUIDs to the number of servers using them. +* Increased the timeout for the decompress files endpoint in the client API from 15 seconds to 15 minutes. + +### Fixed +* Fixed Panel Docker image having a `v` prefix in the version displayed in the admin area. +* Fixed emails using the wrong queue name, causing them to not be sent. +* Fixed the settings keys used for configuring SMTP settings, causing settings to not save properly. +* Fixed the `MAIL_EHLO_DOMAIN` environment variable not being properly backwards compatible with the old `SERVER_NAME` variable. + +## v1.11.1 +### Fixed +* Fixed Panel Docker image showing `canary` as it's version. + +## v1.11.0 +### Changed (since 1.10.4) +* Changed minimum PHP version requirement from `7.4` to `8.0`. +* Upgraded from Laravel 8 to Laravel 9. +* This release requires Wings v1.11.x in order for Server Transfers to work. +* `MB` byte suffixes are now displayed as `MiB` to more accurately reflect the actual value. +* Server re-installation failures are tracked independently of the initial installation process. + +### Fixed (since 1.10.4) +* Node maintenance mode now properly blocks access to servers. +* Fixed the length validation on the Minecraft Forge egg. +* Fixed the password in the JDBC string not being properly URL encoded. +* Fixed an issue where Wings would throw a validation error while attempting to upload activity logs. +* Properly handle a missing `Content-Length` header in the response from the daemon. +* Ensure activity log properties are always returned as an object instead of an empty array. + +### Added (since 1.10.4) +* Added the `server:settings.description` activity log event for when a server description is changed. +* Added the ability to cancel file uploads in the file manager for a server. +* Added a telemetry service to collect anonymous metrics from the panel, this feature is *enabled* by default and can be toggled using the `PTERODACTYL_TELEMETRY_ENABLED` environment variable. + +## v1.11.0-rc.2 +### Changed +* `MB` byte suffixes are now displayed as `MiB` to more accurately reflect the actual value. +* Server re-installation failures are tracked independently of the initial installation process. + +### Fixed +* Properly handle a missing `Content-Length` header in the response from the daemon. +* Ensure activity log properties are always returned as an object instead of an empty array. + +### Added +* Added the `server:settings.description` activity log event for when a server description is changed. +* Added the ability to cancel file uploads in the file manager for a server. +* Added a telemetry service to collect anonymous metrics from the panel, this feature is disabled by default and can be toggled using the `PTERODACTYL_TELEMETRY_ENABLED` environment variable. + +## v1.11.0-rc.1 +### Changed +* Changed minimum PHP version requirement from `7.4` to `8.0`. +* Upgraded from Laravel 8 to Laravel 9. +* This release requires Wings v1.11.x in order for Server Transfers to work. + +### Fixed +* Node maintenance mode now properly blocks access to servers. +* Fixed the length validation on the Minecraft Forge egg. +* Fixed the password in the JDBC string not being properly URL encoded. +* Fixed an issue where Wings would throw a validation error while attempting to upload activity logs. + +## v1.10.4 +### Fixed +* Fixed an issue where subusers could be given permissions that are not actually registered or used. +* Fixed an issue where node FQDNs could not just be IP addresses. + +### Changed +* Change maximum number of API keys per user from `10` to `25`. +* Change byte unit prefix from `B` to `iB` to better reflect our usage of base 2 (multiples of 1024). + +## v1.10.3 +### Fixed +* S3 Backup driver now supports Cloudflare R2. +* Node FQDNs can now be used with AAAA records with no A records present. +* Server transfers can no longer be initiated if the server is being installed, transferred, or restoring a backup. +* Fixed an issue relating to the use of arrays in the `config_files` field with eggs. +* Fixed `oom_disabled` not being mapped in the Application API when creating a new server. + +### Added +* File manager now supports selecting multiple files for upload (when using the upload button). +* Added a configuration option for specifying the S3 storage class for backups. + +### Changed +* Servers will now show the current uptime when the server is starting rather than only showing when the server is marked as online. + +## v1.10.2 +### Fixed +* Fixes a rendering issue with egg descriptions in the admin area +* Fixes the page title on the SSH Keys page + +### Changed +* Additional validation rules will now show a toggle switch rather than an input when editing server variables +* The eggs endpoint will now always return an empty JSON object for the `config_files` field, even if the field is completely empty + +### Added +* Adds a `Force Outgoing IP` option for eggs that can be used to ensure servers making outgoing connections use their allocation IP rather than the node's primary ip +* Adds options to configure sending of email (re)install notifications +* Add an option to configure the part size for backups uploaded to S3 + +## v1.10.1 +### Fixed +* Fixes a surprise `clock()` function that was used for debugging and should not have made it into the release. This was causing activity events to not properly sync between the Panel and Wings. + +## v1.10.0 +### Fixed +* Fixes improper cache key naming on the frontend causing server activity logs to be duplicated across server page views. +* Fixes overflow issues on dialogs when the internal content is too long. +* Fixes spinner overlay on console improperly taking up the entire page making it impossible to use navigation controls. +* Fixes 2FA QR code background being too dark for some phones to properly scan. +* File manager now properly displays an error message if a user attempts to upload a folder rather than files. +* Fixes the "Create Directory" dialog persisting the previously entered value when it is re-opened. + +### Changed +* IP addresses in activity logs are now always displayed to administrators, regardless of if they own the server or not. +* Scroll down indicator on the console has been changed to a down arrow to be clearer. +* Docker builds have been updated to use `PHP 8.1`. +* Recaptcha validation domain is now configurable using the `RECAPTCHA_DOMAIN` environment variable. +* Drag and drop overlay on the file manager has been tweaked to be slightly more consistent with the frontend style and be a little easier to read. + +### Added +* Adds support for the `user_uuid` claim on all generated JWTs which allows Wings to properly identify the user performing each action. +* Adds support for recieving external activity log events from Wings instances (power state, commands, SFTP, and uploads). +* Adds support for tracking failed password-based SFTP logins. +* Server name and description are now passed along to Wings making them available in egg variables for parsing and including. +* Adds support for displaying all active file uploads in the file manager. + +## v1.9.2 +### Fixed +* Fixes rouding in sidebar of CPU usage graph that was causing an excessive number of zeros to be rendered. +* Fixes the Java Version selector modal having the wrong default value selected initially. +* Fixes console rendering in Safari that was causing the console to resize excessively and graphs to overlay content. +* Fixes missing "Starting"/"Stopping" status display in the server uptime block. +* Fixes incorrect formatting of activity log when viewing certain file actions. + +### Changed +* Updated the UI for the two-step authorization setup on accounts to use new Dialog UI and provide better clarity to new users. + +### Added +* Added missing `` tag to template output to avoid entering quirks mode in browsers. +* Added password requirement when enabling TOTP on an account. + +## v1.9.1 +### Fixed +* Fixes missing "Click to Copy" for server address on the console data blocks. +* Fixes data points on the graphs not being properly rounded to two decimal places. +* Returns byte formatting logic to use `1024` as the base value, rather than `1000`. +* Fixes permission error occurring when a server is marked as installing and an admin navigates to the console screen. +* Fixes improper display of install/transfer warning on the server console page. +* Fixes permission matching for the server settings page to correctly allow access when a user has _any_ of the needed permissions. + +### Changed +* Moves the server data blocks to the right-hand side of the console, rather than the left. +* Rather than defaulting graph values at `0` when resetting or refreshing the page, their values are now hidden entirely. +* **[security]** Hides IP addresses from all activity log entries that are not directly associated with the currently signed in user. + +### Added +* Adds the current resource limits for a server next to each data block on the console screen. + +## v1.9.0 +### Added +* Added support for using Tailwind classes inside components using `className={}` rather than having to use `twin.macro` with the `css={}` prop. +* Added HeadlessUI and Heroicons packages. +* Added new `Tooltip.tsx` component to support displaying tooltips within the Panel. +* Adds a new activity log view for both user accounts and individual servers. This builds upon data collected in previous releases. +* Added a new column `api_key_id` to the `activity_logs` table to indicate if the user performed the action while using an API key. +* Adds initial support for language translations on the front-end. The underlying implementation details are working, however work has not yet begun on actually translating all of the strings yet. Expect this to continue in future releases. +* Improved accessibility for navigation icons by adding a tooltip on hover to indicate what each one does. +* Adds logging for API keys that are blocked from performing an API action due to IP address limiting. +* Adds support for `?filter[description]=foo` when querying servers on both the client and application API. + +### Changed +* Updated how release assets are generated to perform more logical bundle splitting. This should help reduce the amount of data users have to download at once in order to render the UI. +* Upgraded From TailwindCSS 2 to 3 — for most people this should have minimal if any impact. +* Chart.js updated from v2 to v3. +* Reduced the number of custom colors in use — by default we now use Tailwind's default color pallet, with the exception of a custom gray scheme. +* **[deprecated]** The use of `neutral` and `primary` have been deprecated in class names, prefer `gray` and `blue` respectively. +* Begins the process of dropping the use of Gravatars for user avatars and replaces them with dynamically generated SVG images. +* Improved front-end route definitions to make it easier for external modifications to inject their routes and components into the codebase without having to modify as many core files. +* Redesigned the server console screen to better display data users might be looking for, and increase the height of the console itself. +* Merged the two network data graphs into a single dual-line graph to better display incoming and outgoing data volumes. +* Updated all byte formatting logic to use `1000` as the divisor rather than `1024` to be more consistent with what users most likely expect. +* Changed the underlying `eslint` rules applied to the front-end codebase to simplify them dramatically. We now utilize `prettier` in combination with some basic default rulesets to make it easier to understand the expected formatting. + +### Fixed +* Fixes a bug causing a 404 error when attempting to delete a database from a server in the admin control panel. +* Fixes console input auto-capitalizing and auto-correcting when entering text on some mobile devices. +* Fixes SES service configuration using a hard-coded `us-east-1` region. +* Fixes a bug causing a 404 error when attempting to delete an SSH key from your account when the SHA256 hash includes a slash. +* Fixes mobile keyboards automatically attempting to capitalize and spellcheck typing on the server console. +* Fixes improper support for IP address CIDR ranges when creating API keys for the client area. +* Fixes a bug preventing additional included details from being returned from the application API when utilizing a client API key as an administrator. + +## v1.8.1 +### Fixed +* Fixes a bug causing mounts to return a 404 error when adding them to a server. +* Fixes a bug causing the Egg Image dropdown to not display properly when creating a new server. +* Fixes a bug causing an error when attemping to create a new server via the API. + +## v1.8.0 +**Important:** this version updates the `version` field on generated Eggs to be `PTDL_v2` due to formatting changes. This +should be completely seamless for most installations as the Panel is able to convert between the two. Custom solutions +using these eggs should be updated to account for the new format. + +This release also changes API key behavior — "client" keys belonging to admin users can now be used to access +the `/api/application` endpoints in their entirety. Existing "application" keys generated in the admin area should +be considered deprecated, but will continue to work. Application keys _will not_ work with the client API. + +### Fixed +* Schedules are no longer run when a server is suspended or marked as installing. +* The remote field when creating a database is no longer limited to an IP address and `%` wildcard — all expected MySQL remote host values are allowed. +* Allocations cannot be deleted from a server by a user if the server is configured with an `allocation_limit` set to `0`. +* The Java Version modal no longer shows a dropdown and update option to users that do not have permission to make those changes. +* The Java Version modal now correctly returns only the images available to the server's selected Egg. +* Fixes leading and trailing spaces being removed from variable values on file manager endpoints, causing errors when trying to perform actions against certain files and folders. + +### Changed +* Forces HTTPS on URLs when the `APP_URL` value is set and includes `https://` within the URL. This addresses proxy misconfiguration issues that would cause URLs to be generated incorrectly. +* Lowers the default timeout values for requests to Wings instances from 10 seconds to 5 seconds. +* Additional permissions (`CREATE TEMPORARY TABLES`, `CREATE VIEW`, `SHOW VIEW`, `EVENT`, and `TRIGGER`) are granted to users when creating new databases for servers. +* development: removed Laravel Debugbar in favor of Clockwork for debugging. +* The 2FA input field when logging in is now correctly identified as `one-time-password` to help browser autofill capabilities. +* Changed API authentication mechanisms to make use of Laravel Sanctum to significantly clean up our internal handling of sessions. +* API keys generated by the system now set a prefix to identify them as Pterodactyl API keys, and if they are client or application keys. This prefix looks like `ptlc_` for client keys, and `ptla_` for application keys. Existing API keys are unaffected by this change. + +### Added +* Added support for PHP 8.1 in addition to PHP 8.0 and 7.4. +* Adds more support for catching potential PID exhaustion errors in different games. +* It is now possible to create a new node on the Panel using an artisan command. +* A new cron cheatsheet has been added which appears when creating a schedule. +* Adds support for filtering the `/api/application/nodes/:id/allocations` endpoint using `?filter[server_id]=0` to only return allocations that are not currently assigned to a server on that node. +* Adds support for naming docker image values in an Egg to improve front-end display capabilities. +* Adds command to return the configuration for a specific node in both YAML and JSON format (`php artisan p:node:configuration`). +* Adds command to return a list of all nodes available on the Panel in both table and JSON format (`php artisan p:node:list`). +* Adds server network (inbound/outbound) usage graphs to the console screen. +* Adds support for configuring CORS on the API by setting the `APP_CORS_ALLOWED_ORIGINS=example.com,dashboard.example.com` environment variable. By default all instances are configured with this set to `*` which allows any origin. +* Adds proper activity logging for the following areas of the Panel: authentication, user account modifications, server modification. This is an initial test implementation before further roll-out in the software. Events are logged into the database but are not currently exposed in the UI — they will be displayed in a future update. + +### Removed +* Removes Google Analytics from the front end code. +* Removes multiple middleware that were previously used for configuring API access and controlling model fetching. This has all been replaced with Laravel Sanctum and standard Laravel API tooling. This should make codebase discovery significantly more simple. +* **DEPRECATED**: The use of `Pterodactyl\Models\AuditLog` is deprecated and all references to this model have been removed from the codebase. In the next major release this model and table will be fully dropped. + +## v1.7.0 +### Fixed +* Fixes typo in message shown to user when deleting a database. +* Fixes formatting of IPv6 addresses when displaying allocations to users. +* Fixes an exception thrown while trying to return error messages from API endpoints that inproperly masked the true underlying error. +* Fixes SSL certificate path generation for Let's Encrypt by ensuring they are always transformed to lowercase. +* Removes duplicate entries when creating a nested folder in the file manager. +* Fixes missing validation of Egg Author email addresses during the setup process that could cause unexpected failures later on. +* Fixes font rendering issues of the console on Firefox due to an outdated version of xterm.js being used. +* Fixes display overlap issues of the two-factor configuration form in a user's settings. +* **[security]** When authenticating using an API key a user session is now only persisted for the duration of the request before being destroyed. + +### Changed +* CPU graph changed to show the maximum amount of CPU available to a server to better match how the memory graph is displayed. + +### Added +* Adds support for `DB_PORT` environment variable in the Docker enterpoint for the Panel image. +* Adds suport for ARM environments in the Docker image. +* Adds a new warning modal for Steam servers shown when an invalid Game Server Login Token (GSL Token) is detected. +* Adds a new warning modal for Steam servers shown when the installation process runs out of available disk space. +* Adds a new warning modal for Minecraft servers shown when a server exceeds the maximum number of child processes. +* Adds support for displaying certain server variable fields as a checkbox when they're detected as using `boolean` or `in:0,1` validation rules. +* Adds support for Pug and Jade in the file editor. +* Adds an entry to the `robots.txt` file to correctly disallow all bot indexing. + + +## v1.6.6 +### Fixed +* **[security]** Fixes a CSRF vulnerability for both the administrative test email endpoint and node auto-deployment token generation endpoint. [GHSA-wwgq-9jhf-qgw6](https://github.com/pterodactyl/panel/security/advisories/GHSA-wwgq-9jhf-qgw6) + +### Changed +* Updates Minecraft eggs to include latest Java 17 yolk by default. + +## v1.6.5 +### Fixed +* Fixes broken application API endpoints due to changes introduced with session management in 1.6.4. + +## v1.6.4 +_This release should not be used, please use `1.6.5`. It has been pulled from our releases._ + +### Fixed +* Fixes a session management bug that would cause a user who signs out of one browser to be unintentionally logged out of other browser sessions when using the client API. + +## v1.6.3 +### Fixed +* **[Security]** Changes logout endpoint to be a POST request with CSRF-token validation to prevent a malicious actor from triggering a user logout. +* Fixes Wings receiving the wrong server suspension state when syncing servers. + +### Added +* Adds additional throttling to login and password reset endpoints. +* Adds server uptime display when viewing a server console. + +## v1.6.2 +### Fixed +* **[Security]** Fixes an authentication bypass vulerability that could allow a malicious actor to login as another user in the Panel without knowing that user's email or password. + +## v1.6.1 +### Fixed +* Fixes server build modifications not being properly persisted to the database when edited. +* Correctly exposes the `oom_disabled` field in the `build` limits block for a server build so that Wings can pick it up. +* +## v1.6.0 +### Fixed +* Fixes array merging logic for server transfers that would cause a 500 error to occur in some scenarios. +* Fixes user password updates not correctly logging the user out and returning a failure message even upon successful update. +* Fixes the count of used backups when browsing a paginated backup list for a server. +* Fixes an error being triggered when API endpoints are called with no `User-Agent` header and an audit log is generated for the action. +* Fixes state management on the frontend not properly resetting the loading indicator when adding subusers to a server. +* Fixes extraneous API calls being made to Wings for the server file listing when not on a file manager screen. + +### Added +* Adds foreign key relationship on the `mount_node`, `mount_server` and `egg_mount` tables. +* Adds environment variable `PER_SCHEDULE_TASK_LIMIT` to allow manual overrides for the number of tasks that can exist on a single schedule. This is currently defaulted to `10`. +* OOM killer can now be configured at the time of server creation. + +### Changed +* Server updates are not dependent on a successful call to Wings occurring — if the API call fails internally the error will be logged but the server update will still be persisted. + +### Removed +* Removed `WingsServerRepository::update()` function — if you were previously using this to modify server elements on Wings please replace calls to it with `::sync()` after updating Wings. + +## v1.5.1 +### Fixed +* Fixes Docker image 404ing instead of being able to access the Panel. +* Fixes Java version feature being only loaded when the `eula` feature is specified. +* Fixes `php artisan p:upgrade` not forcing and seeding while running migrations. +* Fixes spinner overlays overlapping on the server console page. +* Fixes Wings being unable to update backup statuses. + +## v1.5.0 +### Fixed +* Fixes deleting a locked backup that has also been marked as failed to allow deletion rather than returning an error about being locked. +* Fixes server creation process not correctly sending `start_on_completion` to Wings instance. +* Fixes `z-index` on file mass delete modal so it is displayed on top of all elements, rather than hidden under some. +* Supports re-sending requests to the Panel API for backups that are currently marked as failed, allowing a previously failed backup to be marked as successful. +* Minor updates to multiple default eggs for improved error handling and more accurate field-level validation. + +### Updated +* Updates help text for CPU limiting when creating a new server to properly indicate virtual threads are included, rather than only physical threads. +* Updates all of the default eggs shipped with the Panel to reference new [`ghcr.io` yolks repository](https://github.com/pterodactyl/yolks). +* When adding 2FA to an account the key used to generate the token is now displayed to the user allowing them to manually input into their app if necessary. + +### Added +* Adds SSL/TLS options for MySQL and Redis in line with most recent Laravel updates. +* New users created for server MySQL instances will now have the correct permissions for creating foreign keys on tables. +* Adds new automatic popup feature to allow users to quickly update their Minecraft servers to the latest Java® eggs as necessary if unsupported versions are detected. + +### Removed +* Removes legacy `userInteraction` key from eggs which was unused. + +## v1.4.2 +### Fixed +* Fixes logic to disallow creating a backup schedule if the server's backup limit is set to 0. +* Fixes bug preventing a database host from being updated if the linked node is set to "none". +* Fixes files and menus under the "Mass Actions Bar" being unclickable when it is visible. +* Fixes issues with the Teamspeak and Mumble eggs causing installs to fail. +* Fixes automated query to avoid pruning backups that are still running unintentionally. +* Fixes "Delete Server" confirmation modal on the admin screen to actually show up when deleting rather than immediately deleting the server. + +### Added +* Adds support for locking individual server backups to prevent deletion by users or automated backup processes. +* List of files to be deleted is now shown on the delete file confirmation modal. +* Adds support for using `IF` statements in database queries when a database user is created through the Panel. +* Adds support for using a custom mailgun API endpoint rather than only the US based endpoint. +* Adds CPU limit display next to the current CPU usage to match disk and memory usage reporting. +* Adds a "Scroll to Bottom" helper element to the server console when not scrolled to the bottom currently. +* Adds support for querying the API for servers by using the `uuidShort` field rather than only the `uuid` field. + +### Changed +* Updates codebase to use TypeScript 4. +* **[security]**: removes the external dependency for loading QRCode images. They're now generated directly on the frontend using JavaScript. + +## v1.4.1 +### Added +* Adds support for only running a schedule if the server is currently in an online state. +* Adds support for ignoring errors during task execution and continuing on to the next item in the sequence. For example, continuing to a server restart even if sending a command beforehand failed. +* Adds the ability to specify the group to use for file permissions when using the `p:upgrade` command. +* Adds the ability to manually run a schedule even if it is currently disabled. + +## v1.4.0 +### Fixed +* Removes the use of tagging when storing server resource usage in the cache. This addresses errors encountered when using the `file` driver. +* Fixes Wings response handling if Wings returns an error response with a 200-level status code that would improperly be passed back to the client as a successful request. +* Fixes use of JSON specific functions in SQL queries to better support MariaDB users. +* Fixes a migration that could fail on some MySQL/MariaDB setups when trying to encrypt node token values. + +### Changed +* Increases the maximum length allowed for a server name using the Rust egg. +* Updated server resource utilization API call to Wings to use new API response format used by `Wings@1.4.0`. + +## v1.3.2 +### Fixed +* Fixes self-upgrade incorrectly executing the command to un-tar downloaded archives. +* Fixes the checkbox to delete all files when restoring a backup not actually passing that along in the API call. Files will now properly be deleted when restoring if selected. +* Fixes some keybindings not working correctly in the server console on Windows machines. +* Fixes mobile UI incorrectly squishing the Docker image selector on the server settings page. +* Fixes recovery tokens not having a `created_at` value set on them properly when they are created. +* Fixes flawed migration that would not correctly set the month value into schedule crons. +* Fixes incorrect mounting for Docker compose file that would cause error logs to be missing. + +### Changed +* Server resource lookups are now cached on the Panel for 20 seconds at a time to reduce the load from multiple clients requesting the same server's stats. +* Bungeecord egg no longer force-enables the query listener. +* Adds page to the dashboard URL to allow easy loading of a specific pagination page rather than resetting back to the first page when refreshing. +* All application API endpoints now correctly support the `?per_page=N` query parameter to specify how many resources to return at once. + +## v1.3.1 +### Fixed +* Fixes the Rust egg not properly seeding during the upgrade & installation process. +* Fixes backups not being downloadable via the frontend. +* Fixes backup listing showing the wrong number of existing backups based on the current page you're on. + +## v1.3.0 +### Fixed +* Fixes administrator "Other Servers" toggle being persisted wrongly when signing out and signing into a non-administrator account on the server dashboard. +* Fixes composer failing to run properly in local environments where there is no database connection available once configured. +* Fixes SQL exception caused by the Panel attempting to store null values in the database. +* Fixes validation errors caused by improper defaults when trying to edit system settings in the admin area. +* Fixes console overflow when using smaller-than-default font sizes in Firefox. +* Fixes console text input field having a white background when manually building new assets from the release build due to a missing `babel-macros` definition file. +* Fixes database improperly using a signed `smallint` field rather than an unsigned field which restricted SFTP ports to 32767 or less. +* Fixes server console resize handler to no longer encounter an exception at random that breaks the entire UI. +* Fixes unhandled error caused by entering an invalid IP address or FQDN when creating a new node allocation. +* Fixes unhandled error when Wings would fetch a server configuration from the Panel that uses an Egg with invalid JSON data for the configuration fields. +* Fixes email not being sent to a user when their server is done installing. + +### Added +* Adds support for automatically copying SFTP connection details when clicking into the text field. +* Messaging about a node not having any allocations available for deployment has been adjusted to be more understandable by users. +* Adds automated self-upgrade process for Pterodactyl Panel once this version is installed on servers. This allows users to update by using a single command. +* Adds support for specifying a month when creating or modifying a server schedule. +* Adds support for restoring backups (including those in S3 buckets) to a server and optionally deleting all existing files when doing so. +* Adds underlying support for audit logging on servers. Currently this is only used by some internal functionality but will be slowly expanded as time permits to allow more robust logging. +* Adds logic to automatically reset failed server states when Wings is rebooted. This will kick servers out of "installing" and "restoring from backup" states automatically. + +### Changed +* Updated to `Laravel 8` and bumped minimum PHP version from `7.3` to `7.4` with PHP `8.0` being the recommended. +* Server state is now stored in a single `status` column within the database rather than multiple different `tinyint` columns. + +## v1.2.2 +### Fixed +* **[security]** Fixes authentication bypass allowing a user to take control of specific server actions such as executing schedules, rotating database passwords, and viewing or deleting a backup. + +## v1.2.1 +### Fixed +* Fixes URL-encoding of filenames when working in the filemanager to fix issues when moving, renaming, or deleting files. +* Fixes URL-encoding of email addresses when requesting a password reset. + +### Added +* Adds the ability for users to select a base Java Docker image for most Minecraft specific eggs shipped as defaults. + +## v1.2.0 +### Fixed +* Fixes newest backup being deleted when creating a new one using the schedule tasks, rather than the oldest backup. +* Fixes multiple encoding issues when handling file names in the manager. +* Fixes database password not properly being copied to the clipboard when clicked. +* Fixes failed transfers unintentionally locking a server into a failed state and not properly releasing allocations that were reserved. +* Fixes error box on server pages having an oval refresh button rather than a perfect circle. +* Fixes a bunch of errors and usage issues relating to backups especially when uploading to S3-based systems. +* Fixes HMR breaking navigation in development modes on the frontend. + +### Changed +* Updated Paper egg to default to Java 11 as the base docker image. +* Removes the file mode display from the File Manager row listing. +* Updated input UI elements to have thicker borders and more consistent highlighting when active. +* Changed searchbar toggle from `"k"` to `Cmd/Ctrl + "/"` to avoid accidental toggles and be more consistent with other sites. +* Upgrades TailwindCSS to `v2`. + +### Added +* Adds support for eggs to define multiple Docker images that can be selected by users (e.g. Java 8 & 11 images for a single egg). +* Adds support for configuring the default interval for failed backups to be pruned from the system to avoid long running backups being incorrectly cleared. +* Adds server transfer output logging to the server console allowing admins to see how a transfer is progressing directly in the UI. +* Adds client API endpoint to download a file from a remote souce. This functionality is not currently expressed in the UI. + +## v1.1.3 +### Fixed +* Server bulk power actions command will no longer attempt to run commands against installing or suspended servers. +* Fixes the application API throwing an error when attempting to return variables for a server. +* Fixes an error when attempting to install Panel dependencies without specifying an `.env` file due to an unset default timezone. +* Fixes a null value flip in the database migrations. +* Fixes password change endpoint for users allowing a blank value to be provided (even if nothing actually happened). +* Fixes database IP addresses not allowing a `0` in the first octet field. +* Fixes node information being impossible to update if there was a network error during the process. Any errors encountered communicating with Wings are now reported but will not block the actual saving of the changes. +* **[Security]** When 2FA is required on an account the client API endpoints will now properly return an error and the UI will redirect the user to setup 2FA. +* **[Security]** When changing the owner of a server the old owner's JWT is now properly invalidated on Wings. +* Fixes a server error when requesting database information for a server as a subuser and the account is not granted `view_password` permissions. + +### Added +* Adds support for basic backup rotation on a server when creating scheduled backup tasks. +* Makes URLs present in the console clickable. +* Adds `chmod` support to the file manager so that users can manually make modifications to file permissions as they need. + +### Changed +* UI will no longer show a delete button to users when they're editing themselves. +* Updated logic for bulk power actions to no longer run actions against suspended or installing servers. + +## v1.1.2 +### Fixed +* Fixes an exception thrown while trying to validate IP access for the client API. +* Fixes command history scrolling not putting the cursor at the end of the line. +* Fixes file manager rows triggering a 404 when middle-clicked to open in a new tab. + +## v1.1.1 +### Fixed +* Fixes allocation permissions checking on the frontend checking the wrong permission therefore leading to the item never showing up. +* Fixes allocations not updating correctly when added or deleted and switching between pages. + +## v1.1.0 +This release **requires** `Wings@1.1.0` in order to work properly due to breaking internal API changes. + +### Fixed +* Fixes subuser creation/edit modal not submitting correctly when attemping to make modifications. +* Fixes a few remaining issues with multiple egg install scripts. +* Removes the ability for a schedule to have a null name and replaces any existing null names with a randomly generated name. +* Fixes schedules aborting the entire run process if a single schedule encountered an exception. This resolves batches of schedules never running correctly if they occur after a broken schedule. +* Fixes schedules not properly resetting themselves if an exception was encountered during the run. +* Fixes numerous N+1 query run-aways when loading multiple servers via the API. +* Fixes numerous issues with displaying directory and file names in the file manager if they included special characters that could not be decoded properly. +* Fixes CPU pinning not being properly passed along to Wings when updated (this also fixes memory/CPU/disk not passing along correctly as well). +* Fixes spinner not displaying properly when displayed over a modal. + +### Added +* Adds ability for users to generate their own additional server allocations via the frontend if enabled. +* Adds the ability for a user to remove un-needed allocations from their server (as long as it is not the primary allocation). +* Adds support for tracking the last 32 sent console commands for a server. Access the history by using the arrow keys when the command field is active. +* Adds S3 specific environment variables allowing for backups to use any S3 compatiable system, not just AWS. +* Adds support for copying a server allocation address to the clipboard when clicked. +* Adds information about the next schedule run time when viewing an individual schedule. +* Adds link to view a server in the admin control panel to the frontend server view when logged in as a root admin. +* Adds support for egg-specific frontend/backend functionality. This is a beta feature meant for internal features at this time. +* Adds back the EULA warning popup when starting a Minecraft server without an accepted EULA. +* Adds missing descriptions for some user permissions on the frontend UI. + +### Changed +* Adds Save/Invite button to top of subuser edit/creation modal to reduce the need for scrolling. +* Updated language for server transfers and mounts to be less confusing. +* Wings API endpoint for fetching all servers on a node is now properly paginated to reduce system load when returning hundreds or thousands of servers at once. +* Removes unnecessary Wings API calls when adding/editing/deleting mounts. +* Primary allocation for a server is now always returned, even if the subuser does not have permission to view all of the server allocations. +* Google Analytics frontend code is now only loaded when a valid key is provided. + +## v1.0.3 +### Fixed +* Fixes bug causing subusers to not be creatable or editable via the frontend for servers. +* Fixes system timezone not being passed along properly to the MySQL connection causing scheduled tasks to run every minute when the MySQL instance and Panel timezone did not line up. +* Fixes listing servers owned by a user in the admin area to actually list their servers. + +### Changed +* Adds SameSite `lax` attribute for cookies generated by the Panel. +* Adds better filtering for searching servers in the admin area to better key off name, uuid, or owner username/email. + +## v1.0.2 +### Added +* Adds support for searching inside the file editor. +* Adds support for manually executing a schedule regardless of if it is currently queued or not. +* Adds an indicator to the schedule UI to show when a schedule is currently processing. +* Adds support for setting the `backup_limit` of a server via the API. +* **[Security]** Adds login throttling to the 2FA verification endpoint. + +### Fixed +* Fixes subuser page title missing server name. +* Fixes schedule task `sequence_id` not properly being reset when a schedule's task is deleted. +* Fixes misc. UI bugs throughout the frontend when long text overflows its bounds. +* Fixes user deletion command to properly handle email & ID searching. +* Fixes color issues in the terminal causing certain text & background combinations to be illegible. +* Fixes reCAPTCHA not properly resetting on login failure. +* Fixes error messages not properly resetting between login screens. +* Fixes a UI crash when attempting to create or view a directory or file that contained the `%` somewhere in the name. + +### Changed +* Updated the search modal to close itself when the ESC key is pressed. +* Updates the schedule view and editing UI to better display information to users. +* Changed logic powering server searching on the frontend to return more accurate results and return all servers when executing the query as an admin. +* Admin CP link no longer opens in a new tab. +* Mounts will no longer allow a user to mount certain directory combinations. This blocks mounting one server's files into another server, and blocks using the server data directory as a mount destination. +* Cleaned up assorted server build modification code. +* Updates default eggs to have improved install scripts and more consistent container usage. + +## v1.0.1 +### Fixed +* Fixes 500 error when mounting a mount to a server, and other related errors when handling mounts. +* Ensures that `server_transfers` database is deleted if it already exists to avoid unnecessary error. +* Fixes servers getting marked as "not installed" when modifying their startup arguments. +* Fixes filemanager breadcrumbs being set incorrectly when navigating between files and folders. + +### Changed +* Change the requests per minute from 240 to 720 for the client API to avoid unecessarily displaying +"Too Many Requests" errors. +* Added error output to certain commands that will output and terminate the command execution if the database +migrations have not been run correctly for the instance. + +## v1.0.0 +Pterodactyl 1.0 represents the culmination of over two years of work, almost 2,000 commits, endless bug and feature requests, and a dream that +has been in the making since 2013. 🎉 + +Due to the sheer size and timeline of this release I've massively truncated the listing below. There are numerous smaller +bug fixes and changes that would simply be too difficult to keep track of here. Please feel free to browse through the releases +tab for this repository to see more specific changes that have been made. + +### Added +* Adds a new client-facing API allowing a user to control all aspects of their individual servers, or servers +which they have been granted access to as a subuser. +* Adds the ability for backups to be created for a server both manually and via a scheduled task. +* Adds the ability for users to modify their server allocations on the fly and include notes for each allocation. +* Adds the ability for users to generate recovery tokens for 2FA protected logins which can be used in place of +a code should their device be inaccessible. +* Adds support for transfering servers between Nodes via the Panel. +* Adds the ability to assign specific CPU cores to a server (CPU Pinning) process. +* Server owners can now reinstall their assigned server egg automatically with a button on the frontend. + +### Changed +* The entire user frontend has been replaced with a responsive, React backed design implemented using Tailwind CSS. +* Replaces a large amount of complex daemon authentication logic by funneling most API calls through the Panel, and using +JSON Web Tokens where necessary to handle one-time direct authentication with Wings. +* Frontend server listing now includes a toggle to show or hide servers which an administrator has access to, rather +than always showing all servers on the system when logged into an admin account. +* We've replaced Ace Editor on the frontend with a better solution to allow lighter builds and more end-user functionality. +* Server permissions have been overhauled to be both easier to understand in the codebase, and allows plugins to better +hook into the permission system. + +### Removed +* Removes large swaths of code complexity and confusing interface designs that caused a lot of pain to new developers +trying to jump into the codebase. We've simplified this to stick to more established Laravel design standards to make +it easy to parse through the project and make contributions. + +## v0.7.19 (Derelict Dermodactylus) +### Fixed +* **[Security]** Fixes XSS in the admin area's server owner selection. + +## v0.7.18 (Derelict Dermodactylus) +### Fixed +* **[Security]** Re-addressed missed endpoint that would not properly limit a user account to 5 API keys. +* **[Security]** Addresses a Client API vulnerability that would allow a user to list all servers on the system ([`GHSA-6888-7f3w-92jx`](https://github.com/pterodactyl/panel/security/advisories/GHSA-6888-7f3w-92jx)) + ## v0.7.17 (Derelict Dermodactylus) ### Fixed * Limited accounts to 5 API keys at a time. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 443eceab4..eb24ef6bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,44 +1,31 @@ # Contributing -We're glad you want to help us out and make this panel the best that it can be! We have a few simple things to follow when making changes to files and adding new features. -### Project Branches -This section mainly applies to those with read/write access to our repositories, but can be helpful for others. +Pterodactyl does not accept Pull Requests (PRs) _for new functionality_ from users that are not currently part of the +core project team. It has become overwhelming to try and give the proper time and attention that such complicated PRs +tend to require — and deserve. As a result, it is in the project's best interest to limit the scope of work on +new functionality to work done within the core project team. -The `develop` branch should always be in a runnable state, and not contain any major breaking features. For the most part, this means you will need to create `feature/` branches in order to add new functionality or change how things work. When making a feature branch, if it is referencing something in the issue tracker, please title the branch `feature/PTDL-###` where `###` is the issue number. - -Moving forward all commits from contributors should be in the form of a PR, unless it is something we have previously discussed as being able to be pushed right into `develop`. - -All new code should contain unit tests at a minimum (where applicable). There is a lot of uncovered code currently, so as you are doing things please be looking for places that you can write tests. - -### Update the CHANGELOG -When adding something that is new, fixed, changed, or security-related for the next release you should be adding a note to the CHANGELOG. If something is changing within the same version (i.e. fixing a bug introduced but not released) it should _not_ go into the CHANGELOG. - -### Code Guidelines -We are a `PSR-4` and `PSR-0` compliant project, so please follow those guidelines at a minimum. In addition, StyleCI runs on all of our code to ensure the formatting is standardized across everything. When a PR is made StyleCI will analyze your code and make a pull to that branch if necessary to fix any formatting issues. This project also ships with a PHP-CS configuration file and you are welcome to configure your local environment to make use of that. - -All class variable declarations should be in alphabetical order, and constructor arguments should be in alphabetical order based on the classname. See the example below for how this should look, or check out any of the `app/Service` files for examples. - -```php -class ProcessScheduleService -{ - protected $repository; - protected $runnerService; - - public function __construct(RunTaskService $runnerService, ScheduleRepositoryInterface $repository) - { - $this->repository = $repository; - $this->runnerService = $runnerService; - } -``` +PRs that address existing _bugs_ with a corresponding issue opened in our issue tracker will continue to be accepted +and reviewed. Their scope is often significantly more targeted, and simply improving upon existing and well defined +logic. ### Responsible Disclosure -This is a fairly in-depth project and makes use of a lot of parts. We strive to keep everything as secure as possible and welcome you to take a look at the code provided in this project yourself. We do ask that you be considerate of others who are using the software and not publicly disclose security issues without contacting us first by email. -We'll make a deal with you: if you contact us by email and we fail to respond to you within a week you are welcome to publicly disclose whatever issue you have found. We understand how frustrating it is when you find something big and no one will respond to you. This holds us to a standard of providing prompt attention to any issues that arise and keeping this community safe. +This is a fairly in-depth project and makes use of a lot of parts. We strive to keep everything as secure as possible +and welcome you to take a look at the code provided in this project yourself. We do ask that you be considerate of +others who are using the software and not publicly disclose security issues without contacting us first by email. -If you've found what you believe is a security issue please email us at `support@pterodactyl.io`. +We'll make a deal with you: if you contact us by email, and we fail to respond to you within a week you are welcome to +publicly disclose whatever issue you have found. We understand how frustrating it is when you find something big and +no one will respond to you. This holds us to a standard of providing prompt attention to any issues that arise and +keeping this community safe. -### Where to find Us -You can find us in a couple places online. First and foremost, we're active right here on Github. If you encounter a bug or other problems, open an issue on here for us to take a look at it. We also accept feature requests here as well. +If you've found what you believe is a security issue please email `matthew@pterodactyl.io`. Please check +[SECURITY.md](/SECURITY.md) for additional details. -You can also find us on [Discord](https://pterodactyl.io/discord). In the event that you need to get in contact with us privately feel free to contact us at `support@pterodactyl.io`. Try not to email us with requests for support regarding the panel, we'll probably just direct you to our Discord. +### Contact Us + +You can find us in a couple places online. First and foremost, we're active right here on GitHub. If you encounter a +bug or other problems, open an issue on here for us to take a look at it. We also accept feature requests here as well. + +You can also find us on [Discord](https://discord.gg/pterodactyl). diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md deleted file mode 100644 index 906d00d50..000000000 --- a/CONTRIBUTORS.md +++ /dev/null @@ -1,14 +0,0 @@ -# Pterodactyl Panel Contributors -This panel would not be possible without the support of our wonderful community of -developers who provide code enhancements, new features, and bug fixes to make this panel -the best that is can be. You can view a full listing of contributors [here](https://github.com/Pterodactyl/Panel/graphs/contributors). - -Dane Everitt [@DaneEveritt](https://github.com/Pterodactyl/Panel/commits?author=DaneEveritt) - -Dylan Seidt [@DDynamic](https://github.com/Pterodactyl/Panel/commits?author=DDynamic) - -[@nikkiii](https://github.com/Pterodactyl/Panel/commits?author=nikkiii) - -# Get Involved -See our `CONTRIBUTING.md` document for information on how to get started. Once you've submitted some code feel free to -modify this file and add your name to the list. Please follow the format above for your name and linking to your contributions. diff --git a/Containerfile b/Containerfile new file mode 100644 index 000000000..bc922b22e --- /dev/null +++ b/Containerfile @@ -0,0 +1,77 @@ +# Stage 1 - Builder +FROM --platform=$TARGETOS/$TARGETARCH registry.access.redhat.com/ubi9/nodejs-18-minimal AS builder + +USER 0 +RUN npm install -g pnpm + +WORKDIR /var/www/pterodactyl + +COPY --chown=1001:0 public ./public +COPY --chown=1001:0 resources/scripts ./resources/scripts +COPY --chown=1001:0 .eslintignore .eslintrc.js .npmrc .prettierrc.json package.json pnpm-lock.yaml tailwind.config.js tsconfig.json vite.config.ts . + +RUN /opt/app-root/src/.npm-global/bin/pnpm install \ + && /opt/app-root/src/.npm-global/bin/pnpm build \ + && rm -rf resources/scripts .eslintignore .eslintrc.yml .npmrc package.json pnpm-lock.yaml tailwind.config.js tsconfig.json vite.config.ts node_modules + +USER 1001 + +COPY --chown=1001:0 app ./app +COPY --chown=1001:0 bootstrap ./bootstrap +COPY --chown=1001:0 config ./config +COPY --chown=1001:0 database ./database +COPY --chown=1001:0 resources/lang ./resources/lang +COPY --chown=1001:0 resources/views ./resources/views +COPY --chown=1001:0 routes ./routes +COPY --chown=1001:0 .env.example ./.env +COPY --chown=1001:0 artisan CHANGELOG.md composer.json composer.lock LICENSE.md README.md SECURITY.md . + +# Stage 2 - Final +FROM --platform=$TARGETOS/$TARGETARCH registry.access.redhat.com/ubi9/ubi-minimal + +RUN microdnf update -y \ + && rpm --install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm \ + && rpm --install https://rpms.remirepo.net/enterprise/remi-release-9.rpm \ + && microdnf update -y \ + && microdnf install -y ca-certificates shadow-utils tar tzdata unzip wget \ +# ref; https://bugzilla.redhat.com/show_bug.cgi?id=1870814 + && microdnf reinstall -y tzdata \ + && microdnf module -y reset php \ + && microdnf module -y enable php:remi-8.2 \ + && microdnf install -y composer cronie php-{bcmath,cli,common,fpm,gd,gmp,intl,json,mbstring,mysqlnd,opcache,pdo,pecl-redis5,pecl-zip,phpiredis,pgsql,process,sodium,xml,zstd} supervisor \ + && rm /etc/php-fpm.d/www.conf \ + && useradd --home-dir /var/lib/caddy --create-home caddy \ + && mkdir /etc/caddy \ + && wget -O /usr/local/bin/yacron https://github.com/gjcarneiro/yacron/releases/download/0.17.0/yacron-0.17.0-x86_64-unknown-linux-gnu \ + && chmod 755 /usr/local/bin/yacron \ + && microdnf remove -y tar wget \ + && microdnf clean all + +COPY --chown=caddy:caddy --from=builder /var/www/pterodactyl /var/www/pterodactyl + +WORKDIR /var/www/pterodactyl + +RUN mkdir -p /tmp/pterodactyl/cache /tmp/pterodactyl/framework/{cache,sessions,views} storage/framework \ + && rm -rf bootstrap/cache storage/framework/sessions storage/framework/views storage/framework/cache \ + && ln -s /tmp/pterodactyl/cache /var/www/pterodactyl/bootstrap/cache \ + && ln -s /tmp/pterodactyl/framework/cache /var/www/pterodactyl/storage/framework/cache \ + && ln -s /tmp/pterodactyl/framework/sessions /var/www/pterodactyl/storage/framework/sessions \ + && ln -s /tmp/pterodactyl/framework/views /var/www/pterodactyl/storage/framework/views \ + && chmod -R 755 /var/www/pterodactyl/storage/* /tmp/pterodactyl/cache \ + && chown -R caddy:caddy /var/www/pterodactyl /tmp/pterodactyl/{cache,framework} + +USER caddy +ENV USER=caddy + +RUN composer install --no-dev --optimize-autoloader \ + && rm -rf bootstrap/cache/*.php \ + && rm -rf .env storage/logs/*.log + +COPY --from=docker.io/library/caddy:latest /usr/bin/caddy /usr/local/bin/caddy +COPY .github/docker/Caddyfile /etc/caddy/Caddyfile +COPY .github/docker/php-fpm.conf /etc/php-fpm.conf +COPY .github/docker/supervisord.conf /etc/supervisord.conf +COPY .github/docker/yacron.yaml /etc/yacron.yaml + +EXPOSE 8080 +CMD ["/usr/bin/supervisord", "--configuration=/etc/supervisord.conf"] diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 22c151b35..000000000 --- a/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM alpine:3.8 - -WORKDIR /app - -RUN apk add --no-cache --update ca-certificates certbot nginx dcron curl tini php7 php7-bcmath php7-common php7-dom php7-fpm php7-gd php7-mbstring php7-openssl php7-zip php7-pdo php7-phar php7-json php7-pdo_mysql php7-session php7-ctype php7-tokenizer php7-zlib php7-simplexml php7-fileinfo supervisor \ - && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer - -COPY . ./ - -RUN cp .env.example .env \ - && composer install --no-dev --optimize-autoloader \ - && rm .env \ - && chown -R nginx:nginx . && chmod -R 777 storage/* bootstrap/cache - -RUN cp .dev/docker/default.conf /etc/nginx/conf.d/default.conf \ - && cp .dev/docker/www.conf /etc/php7/php-fpm.d/www.conf \ - && cat .dev/docker/supervisord.conf > /etc/supervisord.conf \ - && echo "* * * * * /usr/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \ - && sed -i s/ssl_session_cache/#ssl_session_cache/g /etc/nginx/nginx.conf \ - && mkdir -p /var/run/php /var/run/nginx - -EXPOSE 80 443 - -ENTRYPOINT ["/bin/ash", ".dev/docker/entrypoint.sh"] - -CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..3283b257b --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 Skynet + +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. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 11eabac9f..cb0e2a9d9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,8 @@ # The MIT License (MIT) ``` -Copyright (c) 2015 - 2020 Dane Everitt +Pterodactyl® +Copyright © Dane Everitt and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8ff13e8c8..75bb819fa 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,52 @@ [![Logo Image](https://cdn.pterodactyl.io/logos/new/pterodactyl_logo.png)](https://pterodactyl.io) -[![Build status](https://img.shields.io/travis/pterodactyl/panel/develop.svg?style=flat-square)](https://travis-ci.org/pterodactyl/panel) -[![StyleCI](https://styleci.io/repos/47508644/shield?branch=develop)](https://styleci.io/repos/47508644) -[![Codecov](https://img.shields.io/codecov/c/github/pterodactyl/panel/develop.svg?style=flat-square)](https://codecov.io/gh/Pterodactyl/Panel) -[![Discord](https://img.shields.io/discord/122900397965705216.svg?style=flat-square&label=Discord)](https://pterodactyl.io/discord) +![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/pterodactyl/panel/laravel.yaml?label=Tests&style=for-the-badge&branch=develop) +![Discord](https://img.shields.io/discord/122900397965705216?label=Discord&logo=Discord&logoColor=white&style=for-the-badge) +![GitHub Releases](https://img.shields.io/github/downloads/pterodactyl/panel/latest/total?style=for-the-badge) +![GitHub contributors](https://img.shields.io/github/contributors/pterodactyl/panel?style=for-the-badge) # Pterodactyl Panel -Pterodactyl is the open-source game server management panel built with PHP7, Nodejs, and Go. Designed with security in mind, Pterodactyl runs all game servers in isolated Docker containers while exposing a beautiful and intuitive UI to administrators and users. -What more are you waiting for? Make game servers a first class citizen on your platform today. +Pterodactyl® is a free, open-source game server management panel built with PHP, React, and Go. Designed with security +in mind, Pterodactyl runs all game servers in isolated Docker containers while exposing a beautiful and intuitive +UI to end users. -![Image](https://cdn.pterodactyl.io/site-assets/mockup-macbook-grey.png) +Stop settling for less. Make game servers a first class citizen on your platform. -## Support & Documentation -Support for using Pterodactyl can be found on our [Documentation Website](https://pterodactyl.io/project/introduction.html), [Guides Website](https://guides.pterodactyl.io), or via our [Discord Chat](https://discord.gg/QRDZvVm). +![Image](https://cdn.pterodactyl.io/site-assets/pterodactyl_v1_demo.gif) + +## Documentation + +* [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) +* [Wings Documentation](https://pterodactyl.io/wings/1.0/installing.html) +* [Community Guides](https://pterodactyl.io/community/about.html) +* Or, get additional help [via Discord](https://discord.gg/pterodactyl) + +## Sponsors + +I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's development. +[Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi) + +| Company | About | +|-----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**WISP**](https://wisp.gg) | Extra features. | +| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. | +| [**WemX**](https://wemx.net/) | WemX helps automate your hosting company or SaaS business by automating billing, user management, authentication, and much more. | +| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. | +| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! | +| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa. | +| [**DutchIS**](https://dutchis.net?ref=pterodactyl) | DutchIS provides instant infrastructure such as pay per use VPS hosting. Start your game hosting journey on DutchIS. | +| [**Skoali**](https://skoali.com/) | Skoali is a French company that hosts game servers and other types of services (VPS, WEB, Dedicated servers, ...). We also have a free plan for Minecraft and Garry's Mod. | +| [**Rabbit Computing**](https://www.rabbitcomputing.com/link.php?id=5) | Rabbit Computing offers powerful VPS servers, highly available game hosting, and fully unlimited web hosting. Use code README for 20% off your first three months! | ### Supported Games -We support a huge variety of games by utilizing Docker containers to isolate each instance, giving you the power to host your games across the world without having to bloat each physical machine with additional dependencies. + +Pterodactyl supports a wide variety of games by utilizing Docker containers to isolate each instance. This gives +you the power to run game servers without bloating machines with a host of additional dependencies. Some of our core supported games include: -* Minecraft — including Spigot, Sponge, Bungeecord, Waterfall, and more +* Minecraft — including Paper, Sponge, Bungeecord, Waterfall, and more * Rust * Terraria * Teamspeak @@ -30,46 +56,20 @@ Some of our core supported games include: * Garry's Mod * ARK: Survival Evolved -In addition to our standard nest of supported games, our community is constantly pushing the limits of this software and there are plenty more games available provided by the community. Some of these games include: +In addition to our standard nest of supported games, our community is constantly pushing the limits of this software +and there are plenty more games available provided by the community. Some of these games include: * Factorio * San Andreas: MP * Pocketmine MP * Squad -* FiveM * Xonotic -* Discord ATLBot - -## Credits -This software would not be possible without the work of other open-source authors who provide tools such as: - -[Ace Editor](https://ace.c9.io), [AdminLTE](https://almsaeedstudio.com), [Animate.css](http://daneden.github.io/animate.css/), [AnsiUp](https://github.com/drudru/ansi_up), [Async.js](https://github.com/caolan/async), -[Bootstrap](http://getbootstrap.com), [Bootstrap Notify](http://bootstrap-notify.remabledesigns.com), [Chart.js](http://www.chartjs.org), [FontAwesome](http://fontawesome.io), -[FontAwesome Animations](https://github.com/l-lin/font-awesome-animation), [jQuery](http://jquery.com), [Laravel](https://laravel.com), [Lodash](https://lodash.com), -[Select2](https://select2.github.io), [Socket.io](http://socket.io), [Socket.io File Upload](https://github.com/vote539/socketio-file-upload), [SweetAlert](http://t4t5.github.io/sweetalert), -[Typeahead](https://github.com/bassjobsen/Bootstrap-3-Typeahead), and [Particles.js](http://vincentgarreau.com/particles.js). - -Some Javascript and CSS used within the panel is licensed under a `MIT` or `Apache 2.0` license. Please check their respective header files for more information. +* Starmade +* Discord ATLBot, and most other Node.js/Python discord bots +* [and many more...](https://github.com/parkervcp/eggs) ## License -``` -Copyright (c) 2015 - 2018 Dane Everitt . -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: +Pterodactyl® Copyright © 2015 - 2022 Dane Everitt and contributors. -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. -``` +Code released under the [MIT License](./LICENSE.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e5a9eb0fa --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,20 @@ +# Security Policy + +## Supported Versions + +The following versions of Pterodactyl are receiving active support and maintenance. Any security vulnerabilities discovered must be reproducible in supported versions. + +| Panel | Daemon | Supported | +|--------|--------------|--------------------| +| 1.10.x | wings@1.7.x | :white_check_mark: | +| 1.11.x | wings@1.11.x | :white_check_mark: | +| 0.7.x | daemon@0.6.x | :x: | + + +## Reporting a Vulnerability + +Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can email `matthew@pterodactyl.io`. + +We make every effort to respond as soon as possible, although it may take a day or two for us to sync internally and determine the severity of the report and its impact. Please, _do not_ use a public facing channel or GitHub issues to report sensitive security issues. + +As part of our process, we will create a security advisory for the affected versions and disclose it publicly, usually two to four weeks after a releasing a version that addresses it. diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 1eb62f5dc..000000000 --- a/Vagrantfile +++ /dev/null @@ -1,17 +0,0 @@ -Vagrant.configure("2") do |config| - config.vm.box = "bento/ubuntu-16.04" - - config.vm.synced_folder "./", "/var/www/html/pterodactyl", - owner: "www-data", group: "www-data" - - config.vm.provision :shell, path: ".dev/vagrant/provision.sh" - - config.vm.network :private_network, ip: "192.168.50.2" - config.vm.network :forwarded_port, guest: 80, host: 50080 - config.vm.network :forwarded_port, guest: 8025, host: 58025 - config.vm.network :forwarded_port, guest: 3306, host: 53306 - - # Config for the vagrant-dns plugin (https://github.com/BerlinVagrant/vagrant-dns) - config.dns.tld = "test" - config.dns.patterns = [/^pterodactyl.test$/] -end diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php index 9a10bfd0d..b143719fe 100644 --- a/app/Console/Commands/Environment/AppSettingsCommand.php +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -1,31 +1,22 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Environment; -use DateTimeZone; use Illuminate\Console\Command; use Illuminate\Contracts\Console\Kernel; use Pterodactyl\Traits\Commands\EnvironmentWriterTrait; -use Illuminate\Contracts\Config\Repository as ConfigRepository; class AppSettingsCommand extends Command { use EnvironmentWriterTrait; - const ALLOWED_CACHE_DRIVERS = [ + public const CACHE_DRIVERS = [ 'redis' => 'Redis (recommended)', 'memcached' => 'Memcached', 'file' => 'Filesystem', ]; - const ALLOWED_SESSION_DRIVERS = [ + public const SESSION_DRIVERS = [ 'redis' => 'Redis (recommended)', 'memcached' => 'Memcached', 'database' => 'MySQL Database', @@ -33,30 +24,14 @@ class AppSettingsCommand extends Command 'cookie' => 'Cookie', ]; - const ALLOWED_QUEUE_DRIVERS = [ + public const QUEUE_DRIVERS = [ 'redis' => 'Redis (recommended)', 'database' => 'MySQL Database', 'sync' => 'Sync', ]; - /** - * @var \Illuminate\Contracts\Console\Kernel - */ - protected $command; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var string - */ protected $description = 'Configure basic environment settings for the Panel.'; - /** - * @var string - */ protected $signature = 'p:environment:setup {--new-salt : Whether or not to generate a new salt for Hashids.} {--author= : The email that services created on this instance should be linked to.} @@ -68,25 +43,17 @@ class AppSettingsCommand extends Command {--redis-host= : Redis host to use for connections.} {--redis-pass= : Password used to connect to redis.} {--redis-port= : Port to connect to redis over.} - {--disable-settings-ui}'; + {--settings-ui= : Enable or disable the settings UI.} + {--telemetry= : Enable or disable anonymous telemetry.}'; - /** - * @var array - */ - protected $variables = []; + protected array $variables = []; /** * AppSettingsCommand constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Illuminate\Contracts\Console\Kernel $command */ - public function __construct(ConfigRepository $config, Kernel $command) + public function __construct(private Kernel $console) { parent::__construct(); - - $this->command = $command; - $this->config = $config; } /** @@ -94,60 +61,81 @@ class AppSettingsCommand extends Command * * @throws \Pterodactyl\Exceptions\PterodactylException */ - public function handle() + public function handle(): int { - if (empty($this->config->get('hashids.salt')) || $this->option('new-salt')) { + if (empty(config('hashids.salt')) || $this->option('new-salt')) { $this->variables['HASHIDS_SALT'] = str_random(20); } - $this->output->comment(trans('command/messages.environment.app.author_help')); + $this->output->comment('Provide the email address that eggs exported by this Panel should be from. This should be a valid email address.'); $this->variables['APP_SERVICE_AUTHOR'] = $this->option('author') ?? $this->ask( - trans('command/messages.environment.app.author'), $this->config->get('pterodactyl.service.author', 'unknown@unknown.com') + 'Egg Author Email', + config('pterodactyl.service.author', 'unknown@unknown.com') ); - $this->output->comment(trans('command/messages.environment.app.app_url_help')); + if (!filter_var($this->variables['APP_SERVICE_AUTHOR'], FILTER_VALIDATE_EMAIL)) { + $this->output->error('The service author email provided is invalid.'); + + return 1; + } + + $this->output->comment('The application URL MUST begin with https:// or http:// depending on if you are using SSL or not. If you do not include the scheme your emails and other content will link to the wrong location.'); $this->variables['APP_URL'] = $this->option('url') ?? $this->ask( - trans('command/messages.environment.app.app_url'), $this->config->get('app.url', 'http://example.org') + 'Application URL', + config('app.url', 'https://example.com') ); - $this->output->comment(trans('command/messages.environment.app.timezone_help')); + $this->output->comment('The timezone should match one of PHP\'s supported timezones. If you are unsure, please reference https://php.net/manual/en/timezones.php.'); $this->variables['APP_TIMEZONE'] = $this->option('timezone') ?? $this->anticipate( - trans('command/messages.environment.app.timezone'), - DateTimeZone::listIdentifiers(DateTimeZone::ALL), - $this->config->get('app.timezone') + 'Application Timezone', + \DateTimeZone::listIdentifiers(), + config('app.timezone') ); - $selected = $this->config->get('cache.default', 'redis'); + $selected = config('cache.default', 'redis'); $this->variables['CACHE_DRIVER'] = $this->option('cache') ?? $this->choice( - trans('command/messages.environment.app.cache_driver'), - self::ALLOWED_CACHE_DRIVERS, - array_key_exists($selected, self::ALLOWED_CACHE_DRIVERS) ? $selected : null + 'Cache Driver', + self::CACHE_DRIVERS, + array_key_exists($selected, self::CACHE_DRIVERS) ? $selected : null ); - $selected = $this->config->get('session.driver', 'redis'); + $selected = config('session.driver', 'redis'); $this->variables['SESSION_DRIVER'] = $this->option('session') ?? $this->choice( - trans('command/messages.environment.app.session_driver'), - self::ALLOWED_SESSION_DRIVERS, - array_key_exists($selected, self::ALLOWED_SESSION_DRIVERS) ? $selected : null + 'Session Driver', + self::SESSION_DRIVERS, + array_key_exists($selected, self::SESSION_DRIVERS) ? $selected : null ); - $selected = $this->config->get('queue.default', 'redis'); + $selected = config('queue.default', 'redis'); $this->variables['QUEUE_CONNECTION'] = $this->option('queue') ?? $this->choice( - trans('command/messages.environment.app.queue_driver'), - self::ALLOWED_QUEUE_DRIVERS, - array_key_exists($selected, self::ALLOWED_QUEUE_DRIVERS) ? $selected : null + 'Queue Driver', + self::QUEUE_DRIVERS, + array_key_exists($selected, self::QUEUE_DRIVERS) ? $selected : null ); - if ($this->option('disable-settings-ui')) { - $this->variables['APP_ENVIRONMENT_ONLY'] = 'true'; + if (!is_null($this->option('settings-ui'))) { + $this->variables['APP_ENVIRONMENT_ONLY'] = $this->option('settings-ui') == 'true' ? 'false' : 'true'; } else { - $this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm(trans('command/messages.environment.app.settings'), true) ? 'false' : 'true'; + $this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm('Enable UI based settings editor?', true) ? 'false' : 'true'; + } + + $this->output->comment('Please reference https://pterodactyl.io/panel/1.0/additional_configuration.html#telemetry for more detailed information regarding telemetry data and collection.'); + $this->variables['PTERODACTYL_TELEMETRY_ENABLED'] = $this->option('telemetry') ?? $this->confirm( + 'Enable sending anonymous telemetry data?', + config('pterodactyl.telemetry.enabled', true) + ) ? 'true' : 'false'; + + // Make sure session cookies are set as "secure" when using HTTPS + if (str_starts_with($this->variables['APP_URL'], 'https://')) { + $this->variables['SESSION_SECURE_COOKIE'] = 'true'; } $this->checkForRedis(); $this->writeToEnvironment($this->variables); - $this->info($this->command->output()); + $this->info($this->console->output()); + + return 0; } /** @@ -164,21 +152,22 @@ class AppSettingsCommand extends Command return; } - $this->output->note(trans('command/messages.environment.app.using_redis')); + $this->output->note('You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.'); $this->variables['REDIS_HOST'] = $this->option('redis-host') ?? $this->ask( - trans('command/messages.environment.app.redis_host'), $this->config->get('database.redis.default.host') + 'Redis Host', + config('database.redis.default.host') ); $askForRedisPassword = true; - if (! empty($this->config->get('database.redis.default.password'))) { - $this->variables['REDIS_PASSWORD'] = $this->config->get('database.redis.default.password'); - $askForRedisPassword = $this->confirm(trans('command/messages.environment.app.redis_pass_defined')); + if (!empty(config('database.redis.default.password'))) { + $this->variables['REDIS_PASSWORD'] = config('database.redis.default.password'); + $askForRedisPassword = $this->confirm('It seems a password is already defined for Redis, would you like to change it?'); } if ($askForRedisPassword) { - $this->output->comment(trans('command/messages.environment.app.redis_pass_help')); + $this->output->comment('By default a Redis server instance has no password as it is running locally and inaccessible to the outside world. If this is the case, simply hit enter without entering a value.'); $this->variables['REDIS_PASSWORD'] = $this->option('redis-pass') ?? $this->output->askHidden( - trans('command/messages.environment.app.redis_password') + 'Redis Password' ); } @@ -187,7 +176,8 @@ class AppSettingsCommand extends Command } $this->variables['REDIS_PORT'] = $this->option('redis-port') ?? $this->ask( - trans('command/messages.environment.app.redis_port'), $this->config->get('database.redis.default.port') + 'Redis Port', + config('database.redis.default.port') ); } } diff --git a/app/Console/Commands/Environment/DatabaseSettingsCommand.php b/app/Console/Commands/Environment/DatabaseSettingsCommand.php index 02396142c..fb4a2e25b 100644 --- a/app/Console/Commands/Environment/DatabaseSettingsCommand.php +++ b/app/Console/Commands/Environment/DatabaseSettingsCommand.php @@ -1,48 +1,18 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Environment; -use PDOException; use Illuminate\Console\Command; use Illuminate\Contracts\Console\Kernel; use Illuminate\Database\DatabaseManager; use Pterodactyl\Traits\Commands\EnvironmentWriterTrait; -use Illuminate\Contracts\Config\Repository as ConfigRepository; class DatabaseSettingsCommand extends Command { use EnvironmentWriterTrait; - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Illuminate\Contracts\Console\Kernel - */ - protected $console; - - /** - * @var \Illuminate\Database\DatabaseManager - */ - protected $database; - - /** - * @var string - */ protected $description = 'Configure database settings for the Panel.'; - /** - * @var string - */ protected $signature = 'p:environment:database {--host= : The connection address for the MySQL server.} {--port= : The connection port for the MySQL server.} @@ -50,71 +20,62 @@ class DatabaseSettingsCommand extends Command {--username= : Username to use when connecting.} {--password= : Password to use for this database.}'; - /** - * @var array - */ - protected $variables = []; + protected array $variables = []; /** * DatabaseSettingsCommand constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Illuminate\Database\DatabaseManager $database - * @param \Illuminate\Contracts\Console\Kernel $console */ - public function __construct(ConfigRepository $config, DatabaseManager $database, Kernel $console) + public function __construct(private DatabaseManager $database, private Kernel $console) { parent::__construct(); - - $this->config = $config; - $this->console = $console; - $this->database = $database; } /** * Handle command execution. * - * @return int - * * @throws \Pterodactyl\Exceptions\PterodactylException */ - public function handle() + public function handle(): int { - $this->output->note(trans('command/messages.environment.database.host_warning')); + $this->output->note('It is highly recommended to not use "localhost" as your database host as we have seen frequent socket connection issues. If you want to use a local connection you should be using "127.0.0.1".'); $this->variables['DB_HOST'] = $this->option('host') ?? $this->ask( - trans('command/messages.environment.database.host'), $this->config->get('database.connections.mysql.host', '127.0.0.1') + 'Database Host', + config('database.connections.mysql.host', '127.0.0.1') ); $this->variables['DB_PORT'] = $this->option('port') ?? $this->ask( - trans('command/messages.environment.database.port'), $this->config->get('database.connections.mysql.port', 3306) + 'Database Port', + config('database.connections.mysql.port', 3306) ); $this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask( - trans('command/messages.environment.database.database'), $this->config->get('database.connections.mysql.database', 'panel') + 'Database Name', + config('database.connections.mysql.database', 'panel') ); - $this->output->note(trans('command/messages.environment.database.username_warning')); + $this->output->note('Using the "root" account for MySQL connections is not only highly frowned upon, it is also not allowed by this application. You\'ll need to have created a MySQL user for this software.'); $this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask( - trans('command/messages.environment.database.username'), $this->config->get('database.connections.mysql.username', 'pterodactyl') + 'Database Username', + config('database.connections.mysql.username', 'pterodactyl') ); $askForMySQLPassword = true; - if (! empty($this->config->get('database.connections.mysql.password')) && $this->input->isInteractive()) { - $this->variables['DB_PASSWORD'] = $this->config->get('database.connections.mysql.password'); - $askForMySQLPassword = $this->confirm(trans('command/messages.environment.database.password_defined')); + if (!empty(config('database.connections.mysql.password')) && $this->input->isInteractive()) { + $this->variables['DB_PASSWORD'] = config('database.connections.mysql.password'); + $askForMySQLPassword = $this->confirm('It appears you already have a MySQL connection password defined, would you like to change it?'); } if ($askForMySQLPassword) { - $this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret(trans('command/messages.environment.database.password')); + $this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret('Database Password'); } try { $this->testMySQLConnection(); - } catch (PDOException $exception) { - $this->output->error(trans('command/messages.environment.database.connection_error', ['error' => $exception->getMessage()])); - $this->output->error(trans('command/messages.environment.database.creds_not_saved')); + } catch (\PDOException $exception) { + $this->output->error(sprintf('Unable to connect to the MySQL server using the provided credentials. The error returned was "%s".', $exception->getMessage())); + $this->output->error('Your connection credentials have NOT been saved. You will need to provide valid connection information before proceeding.'); - if ($this->confirm(trans('command/messages.environment.database.try_again'))) { + if ($this->confirm('Go back and try again?')) { $this->database->disconnect('_pterodactyl_command_test'); return $this->handle(); @@ -135,16 +96,16 @@ class DatabaseSettingsCommand extends Command */ private function testMySQLConnection() { - $this->config->set('database.connections._pterodactyl_command_test', [ - 'driver' => 'mysql', - 'host' => $this->variables['DB_HOST'], - 'port' => $this->variables['DB_PORT'], - 'database' => $this->variables['DB_DATABASE'], - 'username' => $this->variables['DB_USERNAME'], - 'password' => $this->variables['DB_PASSWORD'], - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'strict' => true, + config()->set('database.connections._pterodactyl_command_test', [ + 'driver' => 'mysql', + 'host' => $this->variables['DB_HOST'], + 'port' => $this->variables['DB_PORT'], + 'database' => $this->variables['DB_DATABASE'], + 'username' => $this->variables['DB_USERNAME'], + 'password' => $this->variables['DB_PASSWORD'], + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'strict' => true, ]); $this->database->connection('_pterodactyl_command_test')->getPdo(); diff --git a/app/Console/Commands/Environment/EmailSettingsCommand.php b/app/Console/Commands/Environment/EmailSettingsCommand.php index 0a29f2da2..3a211394c 100644 --- a/app/Console/Commands/Environment/EmailSettingsCommand.php +++ b/app/Console/Commands/Environment/EmailSettingsCommand.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Environment; @@ -17,19 +10,8 @@ class EmailSettingsCommand extends Command { use EnvironmentWriterTrait; - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var string - */ protected $description = 'Set or update the email sending configuration for the Panel.'; - /** - * @var string - */ protected $signature = 'p:environment:mail {--driver= : The mail driver to use.} {--email= : Email address that messages from the Panel will originate from.} @@ -37,40 +19,37 @@ class EmailSettingsCommand extends Command {--encryption=} {--host=} {--port=} + {--endpoint=} {--username=} {--password=}'; - /** - * @var array - */ - protected $variables = []; + protected array $variables = []; /** * EmailSettingsCommand constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config */ - public function __construct(ConfigRepository $config) + public function __construct(private ConfigRepository $config) { parent::__construct(); - - $this->config = $config; } /** * Handle command execution. + * * @throws \Pterodactyl\Exceptions\PterodactylException */ public function handle() { $this->variables['MAIL_DRIVER'] = $this->option('driver') ?? $this->choice( - trans('command/messages.environment.mail.ask_driver'), [ + trans('command/messages.environment.mail.ask_driver'), + [ 'smtp' => 'SMTP Server', - 'mail' => 'PHP\'s Internal Mail Function', + 'sendmail' => 'sendmail Binary', 'mailgun' => 'Mailgun Transactional Email', 'mandrill' => 'Mandrill Transactional Email', - 'postmark' => 'Postmarkapp Transactional Email', - ], $this->config->get('mail.driver', 'smtp') + 'postmark' => 'Postmark Transactional Email', + ], + $this->config->get('mail.default', 'smtp') ); $method = 'setup' . studly_case($this->variables['MAIL_DRIVER']) . 'DriverVariables'; @@ -78,16 +57,14 @@ class EmailSettingsCommand extends Command $this->{$method}(); } - $this->variables['MAIL_FROM'] = $this->option('email') ?? $this->ask( - trans('command/messages.environment.mail.ask_mail_from'), $this->config->get('mail.from.address') + $this->variables['MAIL_FROM_ADDRESS'] = $this->option('email') ?? $this->ask( + trans('command/messages.environment.mail.ask_mail_from'), + $this->config->get('mail.from.address') ); $this->variables['MAIL_FROM_NAME'] = $this->option('from') ?? $this->ask( - trans('command/messages.environment.mail.ask_mail_name'), $this->config->get('mail.from.name') - ); - - $this->variables['MAIL_ENCRYPTION'] = $this->option('encryption') ?? $this->choice( - trans('command/messages.environment.mail.ask_encryption'), ['tls' => 'TLS', 'ssl' => 'SSL', '' => 'None'], $this->config->get('mail.encryption', 'tls') + trans('command/messages.environment.mail.ask_mail_name'), + $this->config->get('mail.from.name') ); $this->writeToEnvironment($this->variables); @@ -102,20 +79,29 @@ class EmailSettingsCommand extends Command private function setupSmtpDriverVariables() { $this->variables['MAIL_HOST'] = $this->option('host') ?? $this->ask( - trans('command/messages.environment.mail.ask_smtp_host'), $this->config->get('mail.host') + trans('command/messages.environment.mail.ask_smtp_host'), + $this->config->get('mail.mailers.smtp.host') ); $this->variables['MAIL_PORT'] = $this->option('port') ?? $this->ask( - trans('command/messages.environment.mail.ask_smtp_port'), $this->config->get('mail.port') + trans('command/messages.environment.mail.ask_smtp_port'), + $this->config->get('mail.mailers.smtp.port') ); $this->variables['MAIL_USERNAME'] = $this->option('username') ?? $this->ask( - trans('command/messages.environment.mail.ask_smtp_username'), $this->config->get('mail.username') + trans('command/messages.environment.mail.ask_smtp_username'), + $this->config->get('mail.mailers.smtp.username') ); $this->variables['MAIL_PASSWORD'] = $this->option('password') ?? $this->secret( trans('command/messages.environment.mail.ask_smtp_password') ); + + $this->variables['MAIL_ENCRYPTION'] = $this->option('encryption') ?? $this->choice( + trans('command/messages.environment.mail.ask_encryption'), + ['tls' => 'TLS', 'ssl' => 'SSL', '' => 'None'], + $this->config->get('mail.mailers.smtp.encryption', 'tls') + ); } /** @@ -124,11 +110,18 @@ class EmailSettingsCommand extends Command private function setupMailgunDriverVariables() { $this->variables['MAILGUN_DOMAIN'] = $this->option('host') ?? $this->ask( - trans('command/messages.environment.mail.ask_mailgun_domain'), $this->config->get('services.mailgun.domain') + trans('command/messages.environment.mail.ask_mailgun_domain'), + $this->config->get('services.mailgun.domain') ); $this->variables['MAILGUN_SECRET'] = $this->option('password') ?? $this->ask( - trans('command/messages.environment.mail.ask_mailgun_secret'), $this->config->get('services.mailgun.secret') + trans('command/messages.environment.mail.ask_mailgun_secret'), + $this->config->get('services.mailgun.secret') + ); + + $this->variables['MAILGUN_ENDPOINT'] = $this->option('endpoint') ?? $this->ask( + trans('command/messages.environment.mail.ask_mailgun_endpoint'), + $this->config->get('services.mailgun.endpoint') ); } @@ -138,7 +131,8 @@ class EmailSettingsCommand extends Command private function setupMandrillDriverVariables() { $this->variables['MANDRILL_SECRET'] = $this->option('password') ?? $this->ask( - trans('command/messages.environment.mail.ask_mandrill_secret'), $this->config->get('services.mandrill.secret') + trans('command/messages.environment.mail.ask_mandrill_secret'), + $this->config->get('services.mandrill.secret') ); } @@ -151,7 +145,8 @@ class EmailSettingsCommand extends Command $this->variables['MAIL_HOST'] = 'smtp.postmarkapp.com'; $this->variables['MAIL_PORT'] = 587; $this->variables['MAIL_USERNAME'] = $this->variables['MAIL_PASSWORD'] = $this->option('username') ?? $this->ask( - trans('command/messages.environment.mail.ask_postmark_username'), $this->config->get('mail.username') + trans('command/messages.environment.mail.ask_postmark_username'), + $this->config->get('mail.username') ); } } diff --git a/app/Console/Commands/InfoCommand.php b/app/Console/Commands/InfoCommand.php index d8544477f..6648da298 100644 --- a/app/Console/Commands/InfoCommand.php +++ b/app/Console/Commands/InfoCommand.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands; @@ -15,38 +8,16 @@ use Illuminate\Contracts\Config\Repository as ConfigRepository; class InfoCommand extends Command { - /** - * @var string - */ protected $description = 'Displays the application, database, and email configurations along with the panel version.'; - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var string - */ protected $signature = 'p:info'; - /** - * @var \Pterodactyl\Services\Helpers\SoftwareVersionService - */ - protected $versionService; - /** * VersionCommand constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Services\Helpers\SoftwareVersionService $versionService */ - public function __construct(ConfigRepository $config, SoftwareVersionService $versionService) + public function __construct(private ConfigRepository $config, private SoftwareVersionService $softwareVersionService) { parent::__construct(); - - $this->config = $config; - $this->versionService = $versionService; } /** @@ -56,16 +27,16 @@ class InfoCommand extends Command { $this->output->title('Version Information'); $this->table([], [ - ['Panel Version', $this->config->get('app.version')], - ['Latest Version', $this->versionService->getPanel()], - ['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')], + ['Panel Version', $this->softwareVersionService->getCurrentVersion()], + ['Latest Version', $this->softwareVersionService->getLatestPanel()], + ['Up-to-Date', $this->softwareVersionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')], ['Unique Identifier', $this->config->get('pterodactyl.service.author')], ], 'compact'); $this->output->title('Application Configuration'); $this->table([], [ ['Environment', $this->formatText($this->config->get('app.env'), $this->config->get('app.env') === 'production' ?: 'bg=red')], - ['Debug Mode', $this->formatText($this->config->get('app.debug') ? 'Yes' : 'No', ! $this->config->get('app.debug') ?: 'bg=red')], + ['Debug Mode', $this->formatText($this->config->get('app.debug') ? 'Yes' : 'No', !$this->config->get('app.debug') ?: 'bg=red')], ['Installation URL', $this->config->get('app.url')], ['Installation Directory', base_path()], ['Timezone', $this->config->get('app.timezone')], @@ -81,32 +52,29 @@ class InfoCommand extends Command $driver = $this->config->get('database.default'); $this->table([], [ ['Driver', $driver], - ['Host', $this->config->get("database.connections.{$driver}.host")], - ['Port', $this->config->get("database.connections.{$driver}.port")], - ['Database', $this->config->get("database.connections.{$driver}.database")], - ['Username', $this->config->get("database.connections.{$driver}.username")], + ['Host', $this->config->get("database.connections.$driver.host")], + ['Port', $this->config->get("database.connections.$driver.port")], + ['Database', $this->config->get("database.connections.$driver.database")], + ['Username', $this->config->get("database.connections.$driver.username")], ], 'compact'); + // TODO: Update this to handle other mail drivers $this->output->title('Email Configuration'); $this->table([], [ - ['Driver', $this->config->get('mail.driver')], - ['Host', $this->config->get('mail.host')], - ['Port', $this->config->get('mail.port')], - ['Username', $this->config->get('mail.username')], + ['Driver', $this->config->get('mail.default')], + ['Host', $this->config->get('mail.mailers.smtp.host')], + ['Port', $this->config->get('mail.mailers.smtp.port')], + ['Username', $this->config->get('mail.mailers.smtp.username')], ['From Address', $this->config->get('mail.from.address')], ['From Name', $this->config->get('mail.from.name')], - ['Encryption', $this->config->get('mail.encryption')], + ['Encryption', $this->config->get('mail.mailers.smtp.encryption')], ], 'compact'); } /** * Format output in a Name: Value manner. - * - * @param string $value - * @param string $opts - * @return string */ - private function formatText($value, $opts = '') + private function formatText(string $value, string $opts = ''): string { return sprintf('<%s>%s', $opts, $value); } diff --git a/app/Console/Commands/Location/DeleteLocationCommand.php b/app/Console/Commands/Location/DeleteLocationCommand.php index 77e5606a3..3193e0c3d 100644 --- a/app/Console/Commands/Location/DeleteLocationCommand.php +++ b/app/Console/Commands/Location/DeleteLocationCommand.php @@ -1,59 +1,28 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Location; use Illuminate\Console\Command; +use Illuminate\Support\Collection; use Pterodactyl\Services\Locations\LocationDeletionService; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; class DeleteLocationCommand extends Command { - /** - * @var \Pterodactyl\Services\Locations\LocationDeletionService - */ - protected $deletionService; - - /** - * @var string - */ protected $description = 'Deletes a location from the Panel.'; - /** - * @var \Illuminate\Support\Collection - */ - protected $locations; - - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $repository; - - /** - * @var string - */ protected $signature = 'p:location:delete {--short= : The short code of the location to delete.}'; + protected Collection $locations; + /** * DeleteLocationCommand constructor. - * - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository - * @param \Pterodactyl\Services\Locations\LocationDeletionService $deletionService */ public function __construct( - LocationDeletionService $deletionService, - LocationRepositoryInterface $repository + private LocationDeletionService $deletionService, + private LocationRepositoryInterface $repository ) { parent::__construct(); - - $this->deletionService = $deletionService; - $this->repository = $repository; } /** @@ -66,7 +35,8 @@ class DeleteLocationCommand extends Command { $this->locations = $this->locations ?? $this->repository->all(); $short = $this->option('short') ?? $this->anticipate( - trans('command/messages.location.ask_short'), $this->locations->pluck('short')->toArray() + trans('command/messages.location.ask_short'), + $this->locations->pluck('short')->toArray() ); $location = $this->locations->where('short', $short)->first(); diff --git a/app/Console/Commands/Location/MakeLocationCommand.php b/app/Console/Commands/Location/MakeLocationCommand.php index 791129727..f09f1604e 100644 --- a/app/Console/Commands/Location/MakeLocationCommand.php +++ b/app/Console/Commands/Location/MakeLocationCommand.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Location; @@ -14,33 +7,18 @@ use Pterodactyl\Services\Locations\LocationCreationService; class MakeLocationCommand extends Command { - /** - * @var \Pterodactyl\Services\Locations\LocationCreationService - */ - protected $creationService; - - /** - * @var string - */ protected $signature = 'p:location:make {--short= : The shortcode name of this location (ex. us1).} {--long= : A longer description of this location.}'; - /** - * @var string - */ protected $description = 'Creates a new location on the system via the CLI.'; /** * Create a new command instance. - * - * @param \Pterodactyl\Services\Locations\LocationCreationService $creationService */ - public function __construct(LocationCreationService $creationService) + public function __construct(private LocationCreationService $creationService) { parent::__construct(); - - $this->creationService = $creationService; } /** diff --git a/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php b/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php index 1b5ded4d6..f256fe44f 100644 --- a/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php +++ b/app/Console/Commands/Maintenance/CleanServiceBackupFilesCommand.php @@ -2,34 +2,23 @@ namespace Pterodactyl\Console\Commands\Maintenance; -use SplFileInfo; use Carbon\Carbon; use Illuminate\Console\Command; +use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; class CleanServiceBackupFilesCommand extends Command { - const BACKUP_THRESHOLD_MINUTES = 5; + public const BACKUP_THRESHOLD_MINUTES = 5; - /** - * @var string - */ protected $description = 'Clean orphaned .bak files created when modifying services.'; - /** - * @var \Illuminate\Contracts\Filesystem\Filesystem - */ - protected $disk; - - /** - * @var string - */ protected $signature = 'p:maintenance:clean-service-backups'; + protected Filesystem $disk; + /** * CleanServiceBackupFilesCommand constructor. - * - * @param \Illuminate\Contracts\Filesystem\Factory $filesystem */ public function __construct(FilesystemFactory $filesystem) { @@ -45,7 +34,7 @@ class CleanServiceBackupFilesCommand extends Command { $files = $this->disk->files('services/.bak'); - collect($files)->each(function (SplFileInfo $file) { + collect($files)->each(function (\SplFileInfo $file) { $lastModified = Carbon::createFromTimestamp($this->disk->lastModified($file->getPath())); if ($lastModified->diffInMinutes(Carbon::now()) > self::BACKUP_THRESHOLD_MINUTES) { $this->disk->delete($file->getPath()); diff --git a/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php new file mode 100644 index 000000000..b7a04f8ee --- /dev/null +++ b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php @@ -0,0 +1,49 @@ +option('prune-age') ?? config('backups.prune_age', 360); + if (!$since || !is_digit($since)) { + throw new \InvalidArgumentException('The "--prune-age" argument must be a value greater than 0.'); + } + + $query = $this->backupRepository->getBuilder() + ->whereNull('completed_at') + ->where('created_at', '<=', CarbonImmutable::now()->subMinutes($since)->toDateTimeString()); + + $count = $query->count(); + if (!$count) { + $this->info('There are no orphaned backups to be marked as failed.'); + + return; + } + + $this->warn("Marking $count uncompleted backups that are older than $since minutes as failed."); + + $query->update([ + 'is_successful' => false, + 'completed_at' => CarbonImmutable::now(), + 'updated_at' => CarbonImmutable::now(), + ]); + } +} diff --git a/app/Console/Commands/Migration/CleanOrphanedApiKeysCommand.php b/app/Console/Commands/Migration/CleanOrphanedApiKeysCommand.php deleted file mode 100644 index b9e007ee7..000000000 --- a/app/Console/Commands/Migration/CleanOrphanedApiKeysCommand.php +++ /dev/null @@ -1,58 +0,0 @@ -repository = $repository; - } - - /** - * Delete all orphaned API keys from the database when upgrading from 0.6 to 0.7. - * - * @return null|void - */ - public function handle() - { - $count = $this->repository->findCountWhere([['key_type', '=', ApiKey::TYPE_NONE]]); - $continue = $this->confirm( - 'This action will remove ' . $count . ' keys from the database. Are you sure you wish to continue?', false - ); - - if (! $continue) { - return null; - } - - $this->info('Deleting keys...'); - $this->repository->deleteWhere([['key_type', '=', ApiKey::TYPE_NONE]]); - $this->info('Keys were successfully deleted.'); - } -} diff --git a/app/Console/Commands/Node/MakeNodeCommand.php b/app/Console/Commands/Node/MakeNodeCommand.php new file mode 100644 index 000000000..2d2623ad7 --- /dev/null +++ b/app/Console/Commands/Node/MakeNodeCommand.php @@ -0,0 +1,69 @@ +option('name') ?? $this->ask('Enter a short identifier used to distinguish this node from others'); + $data['description'] = $this->option('description') ?? $this->ask('Enter a description to identify the node'); + $data['location_id'] = $this->option('locationId') ?? $this->ask('Enter a valid location id'); + $data['scheme'] = $this->option('scheme') ?? $this->anticipate( + 'Please either enter https for SSL or http for a non-ssl connection', + ['https', 'http'], + 'https' + ); + $data['fqdn'] = $this->option('fqdn') ?? $this->ask('Enter a domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node'); + $data['public'] = $this->option('public') ?? $this->confirm('Should this node be public? As a note, setting a node to private you will be denying the ability to auto-deploy to this node.', true); + $data['behind_proxy'] = $this->option('proxy') ?? $this->confirm('Is your FQDN behind a proxy?'); + $data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm('Should maintenance mode be enabled?'); + $data['memory'] = $this->option('maxMemory') ?? $this->ask('Enter the maximum amount of memory'); + $data['memory_overallocate'] = $this->option('overallocateMemory') ?? $this->ask('Enter the amount of memory to over allocate by, -1 will disable checking and 0 will prevent creating new servers'); + $data['disk'] = $this->option('maxDisk') ?? $this->ask('Enter the maximum amount of disk space'); + $data['disk_overallocate'] = $this->option('overallocateDisk') ?? $this->ask('Enter the amount of memory to over allocate by, -1 will disable checking and 0 will prevent creating new server'); + $data['upload_size'] = $this->option('uploadSize') ?? $this->ask('Enter the maximum filesize upload', '100'); + $data['daemonListen'] = $this->option('daemonListeningPort') ?? $this->ask('Enter the wings listening port', '8080'); + $data['daemonSFTP'] = $this->option('daemonSFTPPort') ?? $this->ask('Enter the wings SFTP listening port', '2022'); + $data['daemonBase'] = $this->option('daemonBase') ?? $this->ask('Enter the base folder', '/var/lib/pterodactyl/volumes'); + + $node = $this->creationService->handle($data); + $this->line('Successfully created a new node on the location ' . $data['location_id'] . ' with the name ' . $data['name'] . ' and has an id of ' . $node->id . '.'); + } +} diff --git a/app/Console/Commands/Node/NodeConfigurationCommand.php b/app/Console/Commands/Node/NodeConfigurationCommand.php new file mode 100644 index 000000000..9bf0f425c --- /dev/null +++ b/app/Console/Commands/Node/NodeConfigurationCommand.php @@ -0,0 +1,44 @@ +argument('node')) ? 'id' : 'uuid'; + + /** @var \Pterodactyl\Models\Node $node */ + $node = Node::query()->where($column, $this->argument('node'))->firstOr(function () { + $this->error('The selected node does not exist.'); + + exit(1); + }); + + $format = $this->option('format'); + if (!in_array($format, ['yaml', 'yml', 'json'])) { + $this->error('Invalid format specified. Valid options are "yaml" and "json".'); + + return 1; + } + + if ($format === 'json') { + $this->output->write($node->getJsonConfiguration(true)); + } else { + $this->output->write($node->getYamlConfiguration()); + } + + $this->output->newLine(); + + return 0; + } +} diff --git a/app/Console/Commands/Node/NodeListCommand.php b/app/Console/Commands/Node/NodeListCommand.php new file mode 100644 index 000000000..718ddd0d2 --- /dev/null +++ b/app/Console/Commands/Node/NodeListCommand.php @@ -0,0 +1,34 @@ +with('location')->get()->map(function (Node $node) { + return [ + 'id' => $node->id, + 'uuid' => $node->uuid, + 'name' => $node->name, + 'location' => $node->location->short, + 'host' => $node->getConnectionAddress(), + ]; + }); + + if ($this->option('format') === 'json') { + $this->output->write($nodes->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } else { + $this->table(['ID', 'UUID', 'Name', 'Location', 'Host'], $nodes->toArray()); + } + + $this->output->newLine(); + + return 0; + } +} diff --git a/app/Console/Commands/Overrides/KeyGenerateCommand.php b/app/Console/Commands/Overrides/KeyGenerateCommand.php index aa9294999..bc8a4edb6 100644 --- a/app/Console/Commands/Overrides/KeyGenerateCommand.php +++ b/app/Console/Commands/Overrides/KeyGenerateCommand.php @@ -12,13 +12,13 @@ class KeyGenerateCommand extends BaseKeyGenerateCommand */ public function handle() { - if (! empty(config('app.key')) && $this->input->isInteractive()) { - $this->output->warning(trans('command/messages.key.warning')); - if (! $this->confirm(trans('command/messages.key.confirm'))) { + if (!empty(config('app.key')) && $this->input->isInteractive()) { + $this->output->warning('It appears you have already configured an application encryption key. Continuing with this process with overwrite that key and cause data corruption for any existing encrypted data. DO NOT CONTINUE UNLESS YOU KNOW WHAT YOU ARE DOING.'); + if (!$this->confirm('I understand the consequences of performing this command and accept all responsibility for the loss of encrypted data.')) { return; } - if (! $this->confirm(trans('command/messages.key.final_confirm'))) { + if (!$this->confirm('Are you sure you wish to continue? Changing the application encryption key WILL CAUSE DATA LOSS.')) { return; } } diff --git a/app/Console/Commands/Overrides/SeedCommand.php b/app/Console/Commands/Overrides/SeedCommand.php new file mode 100644 index 000000000..7b7b5edb7 --- /dev/null +++ b/app/Console/Commands/Overrides/SeedCommand.php @@ -0,0 +1,26 @@ +hasCompletedMigrations()) { + $this->showMigrationWarning(); + + return 1; + } + + return parent::handle(); + } +} diff --git a/app/Console/Commands/Overrides/UpCommand.php b/app/Console/Commands/Overrides/UpCommand.php new file mode 100644 index 000000000..0a7caaeb7 --- /dev/null +++ b/app/Console/Commands/Overrides/UpCommand.php @@ -0,0 +1,26 @@ +hasCompletedMigrations()) { + $this->showMigrationWarning(); + + return 1; + } + + return parent::handle(); + } +} diff --git a/app/Console/Commands/Schedule/ProcessRunnableCommand.php b/app/Console/Commands/Schedule/ProcessRunnableCommand.php index e5a40fb43..d3dd134e5 100644 --- a/app/Console/Commands/Schedule/ProcessRunnableCommand.php +++ b/app/Console/Commands/Schedule/ProcessRunnableCommand.php @@ -1,86 +1,76 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Schedule; -use Cake\Chronos\Chronos; +use Exception; use Illuminate\Console\Command; -use Illuminate\Support\Collection; +use Pterodactyl\Models\Schedule; +use Illuminate\Support\Facades\Log; +use Illuminate\Database\Eloquent\Builder; use Pterodactyl\Services\Schedules\ProcessScheduleService; -use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; class ProcessRunnableCommand extends Command { - /** - * @var string - */ - protected $description = 'Process schedules in the database and determine which are ready to run.'; - - /** - * @var \Pterodactyl\Services\Schedules\ProcessScheduleService - */ - protected $processScheduleService; - - /** - * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface - */ - protected $repository; - - /** - * @var string - */ protected $signature = 'p:schedule:process'; - /** - * ProcessRunnableCommand constructor. - * - * @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService - * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository - */ - public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository) - { - parent::__construct(); - - $this->processScheduleService = $processScheduleService; - $this->repository = $repository; - } + protected $description = 'Process schedules in the database and determine which are ready to run.'; /** * Handle command execution. */ - public function handle() + public function handle(): int { - $schedules = $this->repository->getSchedulesToProcess(Chronos::now()->toAtomString()); + $schedules = Schedule::query() + ->with('tasks') + ->whereRelation('server', fn (Builder $builder) => $builder->whereNull('status')) + ->where('is_active', true) + ->where('is_processing', false) + ->whereRaw('next_run_at <= NOW()') + ->get(); + if ($schedules->count() < 1) { $this->line('There are no scheduled tasks for servers that need to be run.'); - return; + return 0; } $bar = $this->output->createProgressBar(count($schedules)); - $schedules->each(function ($schedule) use ($bar) { - if ($schedule->tasks instanceof Collection && count($schedule->tasks) > 0) { - $this->processScheduleService->handle($schedule); - - if ($this->input->isInteractive()) { - $bar->clear(); - $this->line(trans('command/messages.schedule.output_line', [ - 'schedule' => $schedule->name, - 'hash' => $schedule->hashid, - ])); - } - } - + foreach ($schedules as $schedule) { + $bar->clear(); + $this->processSchedule($schedule); $bar->advance(); $bar->display(); - }); + } $this->line(''); + + return 0; + } + + /** + * Processes a given schedule and logs and errors encountered the console output. This should + * never throw an exception out, otherwise you'll end up killing the entire run group causing + * any other schedules to not process correctly. + * + * @see https://github.com/pterodactyl/panel/issues/2609 + */ + protected function processSchedule(Schedule $schedule) + { + if ($schedule->tasks->isEmpty()) { + return; + } + + try { + $this->getLaravel()->make(ProcessScheduleService::class)->handle($schedule); + + $this->line(trans('command/messages.schedule.output_line', [ + 'schedule' => $schedule->name, + 'hash' => $schedule->hashid, + ])); + } catch (\Throwable|\Exception $exception) { + Log::error($exception, ['schedule_id' => $schedule->id]); + + $this->error("An error was encountered while processing Schedule #$schedule->id: " . $exception->getMessage()); + } } } diff --git a/app/Console/Commands/Server/BulkPowerActionCommand.php b/app/Console/Commands/Server/BulkPowerActionCommand.php index c6b5e435f..4e7ae1af9 100644 --- a/app/Console/Commands/Server/BulkPowerActionCommand.php +++ b/app/Console/Commands/Server/BulkPowerActionCommand.php @@ -2,67 +2,35 @@ namespace Pterodactyl\Console\Commands\Server; +use Pterodactyl\Models\Server; use Illuminate\Console\Command; -use GuzzleHttp\Exception\RequestException; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Validation\ValidationException; use Illuminate\Validation\Factory as ValidatorFactory; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; +use Pterodactyl\Repositories\Wings\DaemonPowerRepository; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class BulkPowerActionCommand extends Command { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface - */ - private $powerRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - - /** - * @var \Illuminate\Validation\Factory - */ - private $validator; - - /** - * @var string - */ protected $signature = 'p:server:bulk-power {action : The action to perform (start, stop, restart, kill)} {--servers= : A comma separated list of servers.} {--nodes= : A comma separated list of nodes.}'; - /** - * @var string - */ protected $description = 'Perform bulk power management on large groupings of servers or nodes at once.'; /** * BulkPowerActionCommand constructor. - * - * @param \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface $powerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Illuminate\Validation\Factory $validator */ - public function __construct( - PowerRepositoryInterface $powerRepository, - ServerRepositoryInterface $repository, - ValidatorFactory $validator - ) { + public function __construct(private DaemonPowerRepository $powerRepository, private ValidatorFactory $validator) + { parent::__construct(); - - $this->powerRepository = $powerRepository; - $this->repository = $repository; - $this->validator = $validator; } /** * Handle the bulk power request. * * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException */ public function handle() { @@ -90,23 +58,19 @@ class BulkPowerActionCommand extends Command throw new ValidationException($validator); } - $count = $this->repository->getServersForPowerActionCount($servers, $nodes); - if (! $this->confirm(trans('command/messages.server.power.confirm', ['action' => $action, 'count' => $count]))) { + $count = $this->getQueryBuilder($servers, $nodes)->count(); + if (!$this->confirm(trans('command/messages.server.power.confirm', ['action' => $action, 'count' => $count])) && $this->input->isInteractive()) { return; } $bar = $this->output->createProgressBar($count); - $servers = $this->repository->getServersForPowerAction($servers, $nodes); - - foreach ($servers as $server) { + $powerRepository = $this->powerRepository; + $this->getQueryBuilder($servers, $nodes)->each(function (Server $server) use ($action, $powerRepository, &$bar) { $bar->clear(); try { - $this->powerRepository - ->setNode($server->node) - ->setServer($server) - ->sendSignal($action); - } catch (RequestException $exception) { + $powerRepository->setServer($server)->send($action); + } catch (DaemonConnectionException $exception) { $this->output->error(trans('command/messages.server.power.action_failed', [ 'name' => $server->name, 'id' => $server->id, @@ -117,8 +81,26 @@ class BulkPowerActionCommand extends Command $bar->advance(); $bar->display(); - } + }); $this->line(''); } + + /** + * Returns the query builder instance that will return the servers that should be affected. + */ + protected function getQueryBuilder(array $servers, array $nodes): Builder + { + $instance = Server::query()->whereNull('status'); + + if (!empty($nodes) && !empty($servers)) { + $instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes); + } elseif (empty($nodes) && !empty($servers)) { + $instance->whereIn('id', $servers); + } elseif (!empty($nodes) && empty($servers)) { + $instance->whereIn('node_id', $nodes); + } + + return $instance->with('node'); + } } diff --git a/app/Console/Commands/Server/RebuildServerCommand.php b/app/Console/Commands/Server/RebuildServerCommand.php deleted file mode 100644 index ac239b1ee..000000000 --- a/app/Console/Commands/Server/RebuildServerCommand.php +++ /dev/null @@ -1,109 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Console\Commands\Server; - -use Webmozart\Assert\Assert; -use Illuminate\Console\Command; -use GuzzleHttp\Exception\RequestException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Services\Servers\ServerConfigurationStructureService; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; - -class RebuildServerCommand extends Command -{ - /** - * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService - */ - protected $configurationStructureService; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonRepository; - - /** - * @var string - */ - protected $description = 'Rebuild a single server, all servers on a node, or all servers on the panel.'; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $repository; - - /** - * @var string - */ - protected $signature = 'p:server:rebuild - {server? : The ID of the server to rebuild.} - {--node= : ID of the node to rebuild all servers on. Ignored if server is passed.}'; - - /** - * RebuildServerCommand constructor. - * - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository - * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - */ - public function __construct( - DaemonServerRepositoryInterface $daemonRepository, - ServerConfigurationStructureService $configurationStructureService, - ServerRepositoryInterface $repository - ) { - parent::__construct(); - - $this->configurationStructureService = $configurationStructureService; - $this->daemonRepository = $daemonRepository; - $this->repository = $repository; - } - - /** - * Handle command execution. - */ - public function handle() - { - $servers = $this->getServersToProcess(); - $bar = $this->output->createProgressBar(count($servers)); - - $servers->each(function ($server) use ($bar) { - $bar->clear(); - $json = array_merge($this->configurationStructureService->handle($server), ['rebuild' => true]); - - try { - $this->daemonRepository->setServer($server)->update($json); - } catch (RequestException $exception) { - $this->output->error(trans('command/messages.server.rebuild_failed', [ - 'name' => $server->name, - 'id' => $server->id, - 'node' => $server->node->name, - 'message' => $exception->getMessage(), - ])); - } - - $bar->advance(); - $bar->display(); - }); - - $this->line(''); - } - - /** - * Return the servers to be rebuilt. - * - * @return \Illuminate\Database\Eloquent\Collection - */ - private function getServersToProcess() - { - Assert::nullOrIntegerish($this->argument('server'), 'Value passed in server argument must be null or an integer, received %s.'); - Assert::nullOrIntegerish($this->option('node'), 'Value passed in node option must be null or integer, received %s.'); - - return $this->repository->getDataForRebuild($this->argument('server'), $this->option('node')); - } -} diff --git a/app/Console/Commands/TelemetryCommand.php b/app/Console/Commands/TelemetryCommand.php new file mode 100644 index 000000000..3e1b0c2f8 --- /dev/null +++ b/app/Console/Commands/TelemetryCommand.php @@ -0,0 +1,34 @@ +output->info('Collecting telemetry data, this may take a while...'); + + VarDumper::dump($this->telemetryCollectionService->collect()); + } +} diff --git a/app/Console/Commands/UpgradeCommand.php b/app/Console/Commands/UpgradeCommand.php new file mode 100644 index 000000000..6d033a51c --- /dev/null +++ b/app/Console/Commands/UpgradeCommand.php @@ -0,0 +1,195 @@ +option('skip-download'); + if (!$skipDownload) { + $this->output->warning('This command does not verify the integrity of downloaded assets. Please ensure that you trust the download source before continuing. If you do not wish to download an archive, please indicate that using the --skip-download flag, or answering "no" to the question below.'); + $this->output->comment('Download Source (set with --url=):'); + $this->line($this->getUrl()); + } + + if (version_compare(PHP_VERSION, '7.4.0') < 0) { + $this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 7.4.0, you have [' . PHP_VERSION . '].'); + } + + $user = 'www-data'; + $group = 'www-data'; + if ($this->input->isInteractive()) { + if (!$skipDownload) { + $skipDownload = !$this->confirm('Would you like to download and unpack the archive files for the latest version?', true); + } + + if (is_null($this->option('user'))) { + $userDetails = posix_getpwuid(fileowner('public')); + $user = $userDetails['name'] ?? 'www-data'; + + if (!$this->confirm("Your webserver user has been detected as [{$user}]: is this correct?", true)) { + $user = $this->anticipate( + 'Please enter the name of the user running your webserver process. This varies from system to system, but is generally "www-data", "nginx", or "apache".', + [ + 'www-data', + 'nginx', + 'apache', + ] + ); + } + } + + if (is_null($this->option('group'))) { + $groupDetails = posix_getgrgid(filegroup('public')); + $group = $groupDetails['name'] ?? 'www-data'; + + if (!$this->confirm("Your webserver group has been detected as [{$group}]: is this correct?", true)) { + $group = $this->anticipate( + 'Please enter the name of the group running your webserver process. Normally this is the same as your user.', + [ + 'www-data', + 'nginx', + 'apache', + ] + ); + } + } + + if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) { + $this->warn('Upgrade process terminated by user.'); + + return; + } + } + + ini_set('output_buffering', '0'); + $bar = $this->output->createProgressBar($skipDownload ? 9 : 10); + $bar->start(); + + if (!$skipDownload) { + $this->withProgress($bar, function () { + $this->line("\$upgrader> curl -L \"{$this->getUrl()}\" | tar -xzv"); + $process = Process::fromShellCommandline("curl -L \"{$this->getUrl()}\" | tar -xzv"); + $process->run(function ($type, $buffer) { + $this->{$type === Process::ERR ? 'error' : 'line'}($buffer); + }); + }); + } + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan down'); + $this->call('down'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> chmod -R 755 storage bootstrap/cache'); + $process = new Process(['chmod', '-R', '755', 'storage', 'bootstrap/cache']); + $process->run(function ($type, $buffer) { + $this->{$type === Process::ERR ? 'error' : 'line'}($buffer); + }); + }); + + $this->withProgress($bar, function () { + $command = ['composer', 'install', '--no-ansi']; + if (config('app.env') === 'production' && !config('app.debug')) { + $command[] = '--optimize-autoloader'; + $command[] = '--no-dev'; + } + + $this->line('$upgrader> ' . implode(' ', $command)); + $process = new Process($command); + $process->setTimeout(10 * 60); + $process->run(function ($type, $buffer) { + $this->line($buffer); + }); + }); + + /** @var \Illuminate\Foundation\Application $app */ + $app = require __DIR__ . '/../../../bootstrap/app.php'; + /** @var \Pterodactyl\Console\Kernel $kernel */ + $kernel = $app->make(Kernel::class); + $kernel->bootstrap(); + $this->setLaravel($app); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan view:clear'); + $this->call('view:clear'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan config:clear'); + $this->call('config:clear'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan migrate --force --seed'); + $this->call('migrate', ['--force' => true, '--seed' => true]); + }); + + $this->withProgress($bar, function () use ($user, $group) { + $this->line("\$upgrader> chown -R {$user}:{$group} *"); + $process = Process::fromShellCommandline("chown -R {$user}:{$group} *", $this->getLaravel()->basePath()); + $process->setTimeout(10 * 60); + $process->run(function ($type, $buffer) { + $this->{$type === Process::ERR ? 'error' : 'line'}($buffer); + }); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan queue:restart'); + $this->call('queue:restart'); + }); + + $this->withProgress($bar, function () { + $this->line('$upgrader> php artisan up'); + $this->call('up'); + }); + + $this->newLine(2); + $this->info('Panel has been successfully upgraded. Please ensure you also update any Wings instances: https://pterodactyl.io/wings/1.0/upgrading.html'); + } + + protected function withProgress(ProgressBar $bar, \Closure $callback) + { + $bar->clear(); + $callback(); + $bar->advance(); + $bar->display(); + } + + protected function getUrl(): string + { + if ($this->option('url')) { + return $this->option('url'); + } + + return sprintf(self::DEFAULT_URL, $this->option('release') ? 'download/v' . $this->option('release') : 'latest/download'); + } +} diff --git a/app/Console/Commands/User/DeleteUserCommand.php b/app/Console/Commands/User/DeleteUserCommand.php index c9a69bee8..bac97ddaf 100644 --- a/app/Console/Commands/User/DeleteUserCommand.php +++ b/app/Console/Commands/User/DeleteUserCommand.php @@ -1,99 +1,71 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\User; +use Pterodactyl\Models\User; use Webmozart\Assert\Assert; use Illuminate\Console\Command; use Pterodactyl\Services\Users\UserDeletionService; -use Pterodactyl\Contracts\Repository\UserRepositoryInterface; class DeleteUserCommand extends Command { - /** - * @var \Pterodactyl\Services\Users\UserDeletionService - */ - protected $deletionService; - - /** - * @var string - */ protected $description = 'Deletes a user from the Panel if no servers are attached to their account.'; - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - protected $repository; - - /** - * @var string - */ protected $signature = 'p:user:delete {--user=}'; /** * DeleteUserCommand constructor. - * - * @param \Pterodactyl\Services\Users\UserDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository */ - public function __construct( - UserDeletionService $deletionService, - UserRepositoryInterface $repository - ) { + public function __construct(private UserDeletionService $deletionService) + { parent::__construct(); - - $this->deletionService = $deletionService; - $this->repository = $repository; } - /** - * @return bool - * @throws \Pterodactyl\Exceptions\DisplayException - */ - public function handle() + public function handle(): int { $search = $this->option('user') ?? $this->ask(trans('command/messages.user.search_users')); - Assert::notEmpty($search, 'Search term must be a non-null value, received %s.'); + Assert::notEmpty($search, 'Search term should be an email address, got: %s.'); + + $results = User::query() + ->where('id', 'LIKE', "$search%") + ->orWhere('username', 'LIKE', "$search%") + ->orWhere('email', 'LIKE', "$search%") + ->get(); - $results = $this->repository->setSearchTerm($search)->all(); if (count($results) < 1) { $this->error(trans('command/messages.user.no_users_found')); if ($this->input->isInteractive()) { return $this->handle(); } - return false; + return 1; } if ($this->input->isInteractive()) { $tableValues = []; foreach ($results as $user) { - $tableValues[] = [$user->id, $user->email, $user->name]; + $tableValues[] = [$user->id, $user->email, $user->username]; } - $this->table(['User ID', 'Email', 'Name'], $tableValues); - if (! $deleteUser = $this->ask(trans('command/messages.user.select_search_user'))) { + $this->table(['User ID', 'Email', 'Username'], $tableValues); + if (!$deleteUser = $this->ask(trans('command/messages.user.select_search_user'))) { return $this->handle(); } } else { if (count($results) > 1) { $this->error(trans('command/messages.user.multiple_found')); - return false; + return 1; } $deleteUser = $results->first(); } - if ($this->confirm(trans('command/messages.user.confirm_delete')) || ! $this->input->isInteractive()) { + if ($this->confirm(trans('command/messages.user.confirm_delete')) || !$this->input->isInteractive()) { $this->deletionService->handle($deleteUser); $this->info(trans('command/messages.user.deleted')); } + + return 0; } } diff --git a/app/Console/Commands/User/DisableTwoFactorCommand.php b/app/Console/Commands/User/DisableTwoFactorCommand.php index f2cab910d..052220114 100644 --- a/app/Console/Commands/User/DisableTwoFactorCommand.php +++ b/app/Console/Commands/User/DisableTwoFactorCommand.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\User; @@ -14,31 +7,16 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface; class DisableTwoFactorCommand extends Command { - /** - * @var string - */ protected $description = 'Disable two-factor authentication for a specific user in the Panel.'; - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - protected $repository; - - /** - * @var string - */ protected $signature = 'p:user:disable2fa {--email= : The email of the user to disable 2-Factor for.}'; /** * DisableTwoFactorCommand constructor. - * - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository */ - public function __construct(UserRepositoryInterface $repository) + public function __construct(private UserRepositoryInterface $repository) { parent::__construct(); - - $this->repository = $repository; } /** diff --git a/app/Console/Commands/User/MakeUserCommand.php b/app/Console/Commands/User/MakeUserCommand.php index 35060b53f..f37de218b 100644 --- a/app/Console/Commands/User/MakeUserCommand.php +++ b/app/Console/Commands/User/MakeUserCommand.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\User; @@ -14,31 +7,16 @@ use Pterodactyl\Services\Users\UserCreationService; class MakeUserCommand extends Command { - /** - * @var \Pterodactyl\Services\Users\UserCreationService - */ - protected $creationService; - - /** - * @var string - */ protected $description = 'Creates a user on the system via the CLI.'; - /** - * @var string - */ protected $signature = 'p:user:make {--email=} {--username=} {--name-first=} {--name-last=} {--password=} {--admin=} {--no-password}'; /** * MakeUserCommand constructor. - * - * @param \Pterodactyl\Services\Users\UserCreationService $creationService */ - public function __construct(UserCreationService $creationService) + public function __construct(private UserCreationService $creationService) { parent::__construct(); - - $this->creationService = $creationService; } /** @@ -52,21 +30,18 @@ class MakeUserCommand extends Command $root_admin = $this->option('admin') ?? $this->confirm(trans('command/messages.user.ask_admin')); $email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email')); $username = $this->option('username') ?? $this->ask(trans('command/messages.user.ask_username')); - $name_first = $this->option('name-first') ?? $this->ask(trans('command/messages.user.ask_name_first')); - $name_last = $this->option('name-last') ?? $this->ask(trans('command/messages.user.ask_name_last')); - if (is_null($password = $this->option('password')) && ! $this->option('no-password')) { + if (is_null($password = $this->option('password')) && !$this->option('no-password')) { $this->warn(trans('command/messages.user.ask_password_help')); $this->line(trans('command/messages.user.ask_password_tip')); $password = $this->secret(trans('command/messages.user.ask_password')); } - $user = $this->creationService->handle(compact('email', 'username', 'name_first', 'name_last', 'password', 'root_admin')); + $user = $this->creationService->handle(compact('email', 'username', 'password', 'root_admin')); $this->table(['Field', 'Value'], [ ['UUID', $user->uuid], ['Email', $user->email], ['Username', $user->username], - ['Name', $user->name], ['Admin', $user->root_admin ? 'Yes' : 'No'], ]); } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index c87cd5394..56343297c 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,27 +2,75 @@ namespace Pterodactyl\Console; +use Ramsey\Uuid\Uuid; +use Pterodactyl\Models\ActivityLog; use Illuminate\Console\Scheduling\Schedule; +use Illuminate\Database\Console\PruneCommand; +use Pterodactyl\Repositories\Eloquent\SettingsRepository; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; +use Pterodactyl\Services\Telemetry\TelemetryCollectionService; +use Pterodactyl\Console\Commands\Schedule\ProcessRunnableCommand; +use Pterodactyl\Console\Commands\Maintenance\PruneOrphanedBackupsCommand; +use Pterodactyl\Console\Commands\Maintenance\CleanServiceBackupFilesCommand; class Kernel extends ConsoleKernel { /** * Register the commands for the application. */ - protected function commands() + protected function commands(): void { $this->load(__DIR__ . '/Commands'); } /** * Define the application's command schedule. - * - * @param \Illuminate\Console\Scheduling\Schedule $schedule */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { - $schedule->command('p:schedule:process')->everyMinute()->withoutOverlapping(); - $schedule->command('p:maintenance:clean-service-backups')->daily(); + // https://laravel.com/docs/10.x/upgrade#redis-cache-tags + $schedule->command('cache:prune-stale-tags')->hourly(); + + // Execute scheduled commands for servers every minute, as if there was a normal cron running. + $schedule->command(ProcessRunnableCommand::class)->everyMinute()->withoutOverlapping(); + $schedule->command(CleanServiceBackupFilesCommand::class)->daily(); + + if (config('backups.prune_age')) { + // Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted. + $schedule->command(PruneOrphanedBackupsCommand::class)->everyThirtyMinutes(); + } + + if (config('activity.prune_days')) { + $schedule->command(PruneCommand::class, ['--model' => [ActivityLog::class]])->daily(); + } + + if (config('pterodactyl.telemetry.enabled')) { + $this->registerTelemetry($schedule); + } + } + + /** + * I wonder what this does. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + private function registerTelemetry(Schedule $schedule): void + { + $settingsRepository = app()->make(SettingsRepository::class); + + $uuid = $settingsRepository->get('app:telemetry:uuid'); + if (is_null($uuid)) { + $uuid = Uuid::uuid4()->toString(); + $settingsRepository->set('app:telemetry:uuid', $uuid); + } + + // Calculate a fixed time to run the data push at, this will be the same time every day. + $time = hexdec(str_replace('-', '', substr($uuid, 27))) % 1440; + $hour = floor($time / 60); + $minute = $time % 60; + + // Run the telemetry collector. + $schedule->call(app()->make(TelemetryCollectionService::class))->description('Collect Telemetry')->dailyAt("$hour:$minute"); } } diff --git a/app/Console/RequiresDatabaseMigrations.php b/app/Console/RequiresDatabaseMigrations.php new file mode 100644 index 000000000..2e5ebe6b2 --- /dev/null +++ b/app/Console/RequiresDatabaseMigrations.php @@ -0,0 +1,55 @@ +getLaravel()->make('migrator'); + + $files = $migrator->getMigrationFiles(database_path('migrations')); + + if (!$migrator->repositoryExists()) { + return false; + } + + if (array_diff(array_keys($files), $migrator->getRepository()->getRan())) { + return false; + } + + return true; + } + + /** + * Throw a massive error into the console to hopefully catch the users attention and get + * them to properly run the migrations rather than ignoring all of the other previous + * errors... + */ + protected function showMigrationWarning(): void + { + $this->getOutput()->writeln(' +| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | +| | +| Your database has not been properly migrated! | +| | +| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | + +You must run the following command to finish migrating your database: + + php artisan migrate --step --force + +You will not be able to use Pterodactyl Panel as expected without fixing your +database state by running the command above. +'); + + $this->getOutput()->error('You must correct the error above before continuing.'); + } +} diff --git a/app/Contracts/Core/ReceivesEvents.php b/app/Contracts/Core/ReceivesEvents.php index a6c1aa10f..dbbad4458 100644 --- a/app/Contracts/Core/ReceivesEvents.php +++ b/app/Contracts/Core/ReceivesEvents.php @@ -8,8 +8,6 @@ interface ReceivesEvents { /** * Handles receiving an event from the application. - * - * @param \Pterodactyl\Events\Event $notification */ public function handle(Event $notification): void; } diff --git a/app/Contracts/Criteria/CriteriaInterface.php b/app/Contracts/Criteria/CriteriaInterface.php index 628aee94e..5edfcdb62 100644 --- a/app/Contracts/Criteria/CriteriaInterface.php +++ b/app/Contracts/Criteria/CriteriaInterface.php @@ -1,24 +1,14 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Criteria; +use Illuminate\Database\Eloquent\Model; use Pterodactyl\Repositories\Repository; interface CriteriaInterface { /** * Apply selected criteria to a repository call. - * - * @param \Illuminate\Database\Eloquent\Model $model - * @param \Pterodactyl\Repositories\Repository $repository - * @return mixed */ - public function apply($model, Repository $repository); + public function apply(Model $model, Repository $repository): mixed; } diff --git a/app/Contracts/Extensions/HashidsInterface.php b/app/Contracts/Extensions/HashidsInterface.php index 39fa7d624..1cf83b4a0 100644 --- a/app/Contracts/Extensions/HashidsInterface.php +++ b/app/Contracts/Extensions/HashidsInterface.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Extensions; @@ -16,11 +9,7 @@ interface HashidsInterface extends VendorHashidsInterface /** * Decode an encoded hashid and return the first result. * - * @param string $encoded - * @param null $default - * @return mixed - * * @throws \InvalidArgumentException */ - public function decodeFirst($encoded, $default = null); + public function decodeFirst(string $encoded, string $default = null): mixed; } diff --git a/app/Contracts/Http/ClientPermissionsRequest.php b/app/Contracts/Http/ClientPermissionsRequest.php new file mode 100644 index 000000000..a99bca35c --- /dev/null +++ b/app/Contracts/Http/ClientPermissionsRequest.php @@ -0,0 +1,13 @@ +. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; diff --git a/app/Contracts/Repository/Attributes/SearchableInterface.php b/app/Contracts/Repository/Attributes/SearchableInterface.php deleted file mode 100644 index f1ab6e804..000000000 --- a/app/Contracts/Repository/Attributes/SearchableInterface.php +++ /dev/null @@ -1,38 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; @@ -17,34 +10,22 @@ interface EggRepositoryInterface extends RepositoryInterface /** * Return an egg with the variables relation attached. * - * @param int $id - * @return \Pterodactyl\Models\Egg - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithVariables(int $id): Egg; /** * Return all eggs and their relations to be used in the daemon API. - * - * @return \Illuminate\Database\Eloquent\Collection */ public function getAllWithCopyAttributes(): Collection; /** * Return an egg with the scriptFrom and configFrom relations loaded onto the model. - * - * @param int|string $value - * @param string $column - * @return \Pterodactyl\Models\Egg */ - public function getWithCopyAttributes($value, string $column = 'id'): Egg; + public function getWithCopyAttributes(int|string $value, string $column = 'id'): Egg; /** - * Return all of the data needed to export a service. - * - * @param int $id - * @return \Pterodactyl\Models\Egg + * Return all the data needed to export a service. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -52,10 +33,6 @@ interface EggRepositoryInterface extends RepositoryInterface /** * Confirm a copy script belongs to the same nest as the item trying to use it. - * - * @param int $copyFromId - * @param int $service - * @return bool */ public function isCopyableScript(int $copyFromId, int $service): bool; } diff --git a/app/Contracts/Repository/EggVariableRepositoryInterface.php b/app/Contracts/Repository/EggVariableRepositoryInterface.php index 77b46f96d..e20080818 100644 --- a/app/Contracts/Repository/EggVariableRepositoryInterface.php +++ b/app/Contracts/Repository/EggVariableRepositoryInterface.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; @@ -16,9 +9,6 @@ interface EggVariableRepositoryInterface extends RepositoryInterface /** * Return editable variables for a given egg. Editable variables must be set to * user viewable in order to be picked up by this function. - * - * @param int $egg - * @return \Illuminate\Support\Collection */ public function getEditableVariables(int $egg): Collection; } diff --git a/app/Contracts/Repository/LocationRepositoryInterface.php b/app/Contracts/Repository/LocationRepositoryInterface.php index e91da6c43..066a2e7c6 100644 --- a/app/Contracts/Repository/LocationRepositoryInterface.php +++ b/app/Contracts/Repository/LocationRepositoryInterface.php @@ -4,29 +4,21 @@ namespace Pterodactyl\Contracts\Repository; use Pterodactyl\Models\Location; use Illuminate\Support\Collection; -use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; -interface LocationRepositoryInterface extends RepositoryInterface, SearchableInterface +interface LocationRepositoryInterface extends RepositoryInterface { /** * Return locations with a count of nodes and servers attached to it. - * - * @return \Illuminate\Support\Collection */ public function getAllWithDetails(): Collection; /** - * Return all of the available locations with the nodes as a relationship. - * - * @return \Illuminate\Support\Collection + * Return all the available locations with the nodes as a relationship. */ public function getAllWithNodes(): Collection; /** - * Return all of the nodes and their respective count of servers for a location. - * - * @param int $id - * @return mixed + * Return all the nodes and their respective count of servers for a location. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -35,9 +27,6 @@ interface LocationRepositoryInterface extends RepositoryInterface, SearchableInt /** * Return a location and the count of nodes in that location. * - * @param int $id - * @return mixed - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithNodeCount(int $id): Location; diff --git a/app/Contracts/Repository/NestRepositoryInterface.php b/app/Contracts/Repository/NestRepositoryInterface.php index b06f9730b..2f80bedd5 100644 --- a/app/Contracts/Repository/NestRepositoryInterface.php +++ b/app/Contracts/Repository/NestRepositoryInterface.php @@ -1,44 +1,29 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; use Pterodactyl\Models\Nest; +use Illuminate\Database\Eloquent\Collection; interface NestRepositoryInterface extends RepositoryInterface { /** - * Return a nest or all nests with their associated eggs, variables, and packs. - * - * @param int $id - * @return \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Nest + * Return a nest or all nests with their associated eggs and variables. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithEggs(int $id = null); + public function getWithEggs(int $id = null): Collection|Nest; /** - * Return a nest or all nests and the count of eggs, packs, and servers for that nest. - * - * @param int|null $id - * @return \Pterodactyl\Models\Nest|\Illuminate\Database\Eloquent\Collection + * Return a nest or all nests and the count of eggs and servers for that nest. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithCounts(int $id = null); + public function getWithCounts(int $id = null): Collection|Nest; /** * Return a nest along with its associated eggs and the servers relation on those eggs. * - * @param int $id - * @return \Pterodactyl\Models\Nest - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithEggServers(int $id): Nest; diff --git a/app/Contracts/Repository/NodeRepositoryInterface.php b/app/Contracts/Repository/NodeRepositoryInterface.php index aef04e2a9..87d5b4de5 100644 --- a/app/Contracts/Repository/NodeRepositoryInterface.php +++ b/app/Contracts/Repository/NodeRepositoryInterface.php @@ -2,74 +2,37 @@ namespace Pterodactyl\Contracts\Repository; -use Generator; use Pterodactyl\Models\Node; use Illuminate\Support\Collection; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; -interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterface +interface NodeRepositoryInterface extends RepositoryInterface { - const THRESHOLD_PERCENTAGE_LOW = 75; - const THRESHOLD_PERCENTAGE_MEDIUM = 90; + public const THRESHOLD_PERCENTAGE_LOW = 75; + public const THRESHOLD_PERCENTAGE_MEDIUM = 90; /** * Return the usage stats for a single node. - * - * @param \Pterodactyl\Models\Node $node - * @return array */ public function getUsageStats(Node $node): array; /** * Return the usage stats for a single node. - * - * @param \Pterodactyl\Models\Node $node - * @return array */ public function getUsageStatsRaw(Node $node): array; - /** - * Return all available nodes with a searchable interface. - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getNodeListingData(): LengthAwarePaginator; - /** * Return a single node with location and server information. - * - * @param \Pterodactyl\Models\Node $node - * @param bool $refresh - * @return \Pterodactyl\Models\Node */ public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node; /** * Attach a paginated set of allocations to a node mode including * any servers that are also attached to those allocations. - * - * @param \Pterodactyl\Models\Node $node - * @param bool $refresh - * @return \Pterodactyl\Models\Node */ public function loadNodeAllocations(Node $node, bool $refresh = false): Node; /** * Return a collection of nodes for all locations to use in server creation UI. - * - * @return \Illuminate\Support\Collection */ public function getNodesForServerCreation(): Collection; - - /** - * Return the IDs of all nodes that exist in the provided locations and have the space - * available to support the additional disk and memory provided. - * - * @param array $locations - * @param int $disk - * @param int $memory - * @return \Generator - */ - public function getNodesWithResourceUse(array $locations, int $disk, int $memory): Generator; } diff --git a/app/Contracts/Repository/PackRepositoryInterface.php b/app/Contracts/Repository/PackRepositoryInterface.php deleted file mode 100644 index 6ef7cef7f..000000000 --- a/app/Contracts/Repository/PackRepositoryInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; diff --git a/app/Contracts/Repository/RepositoryInterface.php b/app/Contracts/Repository/RepositoryInterface.php index 4cdebf7bb..5dc0c6655 100644 --- a/app/Contracts/Repository/RepositoryInterface.php +++ b/app/Contracts/Repository/RepositoryInterface.php @@ -3,208 +3,139 @@ namespace Pterodactyl\Contracts\Repository; use Illuminate\Support\Collection; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Contracts\Pagination\LengthAwarePaginator; interface RepositoryInterface { /** * Return an identifier or Model object to be used by the repository. - * - * @return string|\Closure|object */ - public function model(); + public function model(): string; /** * Return the model being used for this repository instance. - * - * @return mixed */ - public function getModel(); + public function getModel(): Model; /** * Returns an instance of a query builder. - * - * @return mixed */ - public function getBuilder(); + public function getBuilder(): Builder; /** * Returns the columns to be selected or returned by the query. - * - * @return mixed */ - public function getColumns(); + public function getColumns(): array; /** * An array of columns to filter the response by. - * - * @param array|string $columns - * @return $this */ - public function setColumns($columns = ['*']); + public function setColumns(array|string $columns = ['*']): self; /** * Stop repository update functions from returning a fresh * model when changes are committed. - * - * @return $this */ - public function withoutFreshModel(); + public function withoutFreshModel(): self; /** * Return a fresh model with a repository updates a model. - * - * @return $this */ - public function withFreshModel(); + public function withFreshModel(): self; /** - * Set whether or not the repository should return a fresh model + * Set whether the repository should return a fresh model * when changes are committed. - * - * @param bool $fresh - * @return $this */ - public function setFreshModel(bool $fresh = true); + public function setFreshModel(bool $fresh = true): self; /** * Create a new model instance and persist it to the database. * - * @param array $fields - * @param bool $validate - * @param bool $force - * @return mixed - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function create(array $fields, bool $validate = true, bool $force = false); + public function create(array $fields, bool $validate = true, bool $force = false): mixed; /** * Find a model that has the specific ID passed. * - * @param int $id - * @return mixed - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function find(int $id); + public function find(int $id): mixed; /** * Find a model matching an array of where clauses. - * - * @param array $fields - * @return \Illuminate\Support\Collection */ public function findWhere(array $fields): Collection; /** * Find and return the first matching instance for the given fields. * - * @param array $fields - * @return mixed - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function findFirstWhere(array $fields); + public function findFirstWhere(array $fields): mixed; /** * Return a count of records matching the passed arguments. - * - * @param array $fields - * @return int */ public function findCountWhere(array $fields): int; /** * Delete a given record from the database. - * - * @param int $id - * @return int */ public function delete(int $id): int; /** * Delete records matching the given attributes. - * - * @param array $attributes - * @return int */ public function deleteWhere(array $attributes): int; /** * Update a given ID with the passed array of fields. * - * @param int $id - * @param array $fields - * @param bool $validate - * @param bool $force - * @return mixed - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update($id, array $fields, bool $validate = true, bool $force = false); + public function update(int $id, array $fields, bool $validate = true, bool $force = false): mixed; /** * Perform a mass update where matching records are updated using whereIn. * This does not perform any model data validation. - * - * @param string $column - * @param array $values - * @param array $fields - * @return int */ public function updateWhereIn(string $column, array $values, array $fields): int; /** * Update a record if it exists in the database, otherwise create it. * - * @param array $where - * @param array $fields - * @param bool $validate - * @param bool $force - * @return mixed - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false); + public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false): mixed; /** * Return all records associated with the given model. - * - * @return Collection */ public function all(): Collection; /** * Return a paginated result set using a search term if set on the repository. - * - * @param int $perPage - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ public function paginated(int $perPage): LengthAwarePaginator; /** * Insert a single or multiple records into the database at once skipping * validation and mass assignment checking. - * - * @param array $data - * @return bool */ public function insert(array $data): bool; /** * Insert multiple records into the database and ignore duplicates. - * - * @param array $values - * @return bool */ public function insertIgnore(array $values): bool; /** * Get the amount of entries in the database. - * - * @return int */ public function count(): int; } diff --git a/app/Contracts/Repository/ScheduleRepositoryInterface.php b/app/Contracts/Repository/ScheduleRepositoryInterface.php index 67bf40271..fa9c18b41 100644 --- a/app/Contracts/Repository/ScheduleRepositoryInterface.php +++ b/app/Contracts/Repository/ScheduleRepositoryInterface.php @@ -8,38 +8,14 @@ use Illuminate\Support\Collection; interface ScheduleRepositoryInterface extends RepositoryInterface { /** - * Return all of the schedules for a given server. - * - * @param int $server - * @return \Illuminate\Support\Collection + * Return all the schedules for a given server. */ public function findServerSchedules(int $server): Collection; /** - * Load the tasks relationship onto the Schedule module if they are not - * already present. - * - * @param \Pterodactyl\Models\Schedule $schedule - * @param bool $refresh - * @return \Pterodactyl\Models\Schedule - */ - public function loadTasks(Schedule $schedule, bool $refresh = false): Schedule; - - /** - * Return a schedule model with all of the associated tasks as a relationship. - * - * @param int $schedule - * @return \Pterodactyl\Models\Schedule + * Return a schedule model with all the associated tasks as a relationship. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getScheduleWithTasks(int $schedule): Schedule; - - /** - * Return all of the schedules that should be processed. - * - * @param string $timestamp - * @return \Illuminate\Support\Collection - */ - public function getSchedulesToProcess(string $timestamp): Collection; } diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index ba853398e..9294a191b 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -2,46 +2,30 @@ namespace Pterodactyl\Contracts\Repository; -use Pterodactyl\Models\User; use Pterodactyl\Models\Server; use Illuminate\Support\Collection; use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; -interface ServerRepositoryInterface extends RepositoryInterface, SearchableInterface +interface ServerRepositoryInterface extends RepositoryInterface { - /** - * Returns a listing of all servers that exist including relationships. - * - * @param int $paginate - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getAllServers(int $paginate): LengthAwarePaginator; - /** * Load the egg relations onto the server model. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function loadEggRelations(Server $server, bool $refresh = false): Server; /** * Return a collection of servers with their associated data for rebuild operations. - * - * @param int|null $server - * @param int|null $node - * @return \Illuminate\Support\Collection */ public function getDataForRebuild(int $server = null, int $node = null): Collection; + /** + * Return a collection of servers with their associated data for reinstall operations. + */ + public function getDataForReinstall(int $server = null, int $node = null): Collection; + /** * Return a server model and all variables associated with the server. * - * @param int $id - * @return \Pterodactyl\Models\Server - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function findWithVariables(int $id): Server; @@ -50,116 +34,40 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter * Get the primary allocation for a given server. If a model is passed into * the function, load the allocation relationship onto it. Otherwise, find and * return the server from the database. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function getPrimaryAllocation(Server $server, bool $refresh = false): Server; - /** - * Return all of the server variables possible and default to the variable - * default if there is no value defined for the specific server requested. - * - * @param int $id - * @param bool $returnAsObject - * @return array|object - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getVariablesWithValues(int $id, bool $returnAsObject = false); - /** * Return enough data to be used for the creation of a server via the daemon. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function getDataForCreation(Server $server, bool $refresh = false): Server; /** * Load associated databases onto the server model. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function loadDatabaseRelations(Server $server, bool $refresh = false): Server; /** * Get data for use when updating a server on the Daemon. Returns an array of - * the egg and pack UUID which are used for build and rebuild. Only loads relations + * the egg which is used for build and rebuild. Only loads relations * if they are missing, or refresh is set to true. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return array */ public function getDaemonServiceData(Server $server, bool $refresh = false): array; - /** - * Return a paginated list of servers that a user can access at a given level. - * - * @param \Pterodactyl\Models\User $user - * @param int $level - * @param bool|int $paginate - * @return \Illuminate\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection - */ - public function filterUserAccessServers(User $user, int $level, $paginate = 25); - /** * Return a server by UUID. * - * @param string $uuid - * @return \Pterodactyl\Models\Server - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getByUuid(string $uuid): Server; - /** - * Return all of the servers that should have a power action performed against them. - * - * @param int[] $servers - * @param int[] $nodes - * @param bool $returnCount - * @return int|\Generator - */ - public function getServersForPowerAction(array $servers = [], array $nodes = [], bool $returnCount = false); - - /** - * Return the total number of servers that will be affected by the query. - * - * @param int[] $servers - * @param int[] $nodes - * @return int - */ - public function getServersForPowerActionCount(array $servers = [], array $nodes = []): int; - /** * Check if a given UUID and UUID-Short string are unique to a server. - * - * @param string $uuid - * @param string $short - * @return bool */ public function isUniqueUuidCombo(string $uuid, string $short): bool; /** - * Get the amount of servers that are suspended. - * - * @return int - */ - public function getSuspendedServersCount(): int; - - /** - * Returns all of the servers that exist for a given node in a paginated response. - * - * @param int $node - * @param int $limit - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + * Returns all the servers that exist for a given node in a paginated response. */ public function loadAllServersForNode(int $node, int $limit): LengthAwarePaginator; } diff --git a/app/Contracts/Repository/SessionRepositoryInterface.php b/app/Contracts/Repository/SessionRepositoryInterface.php index 8020d15a3..0a433af71 100644 --- a/app/Contracts/Repository/SessionRepositoryInterface.php +++ b/app/Contracts/Repository/SessionRepositoryInterface.php @@ -7,19 +7,12 @@ use Illuminate\Support\Collection; interface SessionRepositoryInterface extends RepositoryInterface { /** - * Return all of the active sessions for a user. - * - * @param int $user - * @return \Illuminate\Support\Collection + * Return all the active sessions for a user. */ public function getUserSessions(int $user): Collection; /** * Delete a session for a given user. - * - * @param int $user - * @param string $session - * @return null|int */ - public function deleteUserSession(int $user, string $session); + public function deleteUserSession(int $user, string $session): ?int; } diff --git a/app/Contracts/Repository/SettingsRepositoryInterface.php b/app/Contracts/Repository/SettingsRepositoryInterface.php index 7d192c80a..c55c09b74 100644 --- a/app/Contracts/Repository/SettingsRepositoryInterface.php +++ b/app/Contracts/Repository/SettingsRepositoryInterface.php @@ -7,9 +7,6 @@ interface SettingsRepositoryInterface extends RepositoryInterface /** * Store a new persistent setting in the database. * - * @param string $key - * @param string|null $value - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -17,17 +14,11 @@ interface SettingsRepositoryInterface extends RepositoryInterface /** * Retrieve a persistent setting from the database. - * - * @param string $key - * @param mixed $default - * @return mixed */ - public function get(string $key, $default); + public function get(string $key, mixed $default): mixed; /** * Remove a key from the database cache. - * - * @param string $key */ public function forget(string $key); } diff --git a/app/Contracts/Repository/SubuserRepositoryInterface.php b/app/Contracts/Repository/SubuserRepositoryInterface.php index 2d722119a..1379e120b 100644 --- a/app/Contracts/Repository/SubuserRepositoryInterface.php +++ b/app/Contracts/Repository/SubuserRepositoryInterface.php @@ -8,29 +8,17 @@ interface SubuserRepositoryInterface extends RepositoryInterface { /** * Return a subuser with the associated server relationship. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @param bool $refresh - * @return \Pterodactyl\Models\Subuser */ public function loadServerAndUserRelations(Subuser $subuser, bool $refresh = false): Subuser; /** * Return a subuser with the associated permissions relationship. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @param bool $refresh - * @return \Pterodactyl\Models\Subuser */ public function getWithPermissions(Subuser $subuser, bool $refresh = false): Subuser; /** * Return a subuser and associated permissions given a user_id and server_id. * - * @param int $user - * @param int $server - * @return \Pterodactyl\Models\Subuser - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithPermissionsUsingUserAndServer(int $user, int $server): Subuser; diff --git a/app/Contracts/Repository/TaskRepositoryInterface.php b/app/Contracts/Repository/TaskRepositoryInterface.php index 11cb704e1..0cd980d21 100644 --- a/app/Contracts/Repository/TaskRepositoryInterface.php +++ b/app/Contracts/Repository/TaskRepositoryInterface.php @@ -9,19 +9,12 @@ interface TaskRepositoryInterface extends RepositoryInterface /** * Get a task and the server relationship for that task. * - * @param int $id - * @return \Pterodactyl\Models\Task - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getTaskForJobProcess(int $id): Task; /** * Returns the next task in a schedule. - * - * @param int $schedule - * @param int $index - * @return null|\Pterodactyl\Models\Task */ - public function getNextTask(int $schedule, int $index); + public function getNextTask(int $schedule, int $index): ?Task; } diff --git a/app/Contracts/Repository/UserRepositoryInterface.php b/app/Contracts/Repository/UserRepositoryInterface.php index 41649f48b..9efd29fac 100644 --- a/app/Contracts/Repository/UserRepositoryInterface.php +++ b/app/Contracts/Repository/UserRepositoryInterface.php @@ -2,24 +2,6 @@ namespace Pterodactyl\Contracts\Repository; -use Illuminate\Support\Collection; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; - -interface UserRepositoryInterface extends RepositoryInterface, SearchableInterface +interface UserRepositoryInterface extends RepositoryInterface { - /** - * Return all users with counts of servers and subusers of servers. - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getAllUsersWithCounts(): LengthAwarePaginator; - - /** - * Return all matching models for a user in a format that can be used for dropdowns. - * - * @param string|null $query - * @return \Illuminate\Support\Collection - */ - public function filterUsersByQuery(?string $query): Collection; } diff --git a/app/Events/ActivityLogged.php b/app/Events/ActivityLogged.php new file mode 100644 index 000000000..ca9db7930 --- /dev/null +++ b/app/Events/ActivityLogged.php @@ -0,0 +1,34 @@ +model->event === $event; + } + + public function actor(): ?Model + { + return $this->isSystem() ? null : $this->model->actor; + } + + public function isServerEvent(): bool + { + return Str::startsWith($this->model->event, 'server:'); + } + + public function isSystem(): bool + { + return is_null($this->model->actor_id); + } +} diff --git a/app/Events/Auth/DirectLogin.php b/app/Events/Auth/DirectLogin.php new file mode 100644 index 000000000..99df4e100 --- /dev/null +++ b/app/Events/Auth/DirectLogin.php @@ -0,0 +1,13 @@ +. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Auth; +use Pterodactyl\Events\Event; use Illuminate\Queue\SerializesModels; -class FailedCaptcha +class FailedCaptcha extends Event { use SerializesModels; - /** - * The IP that the request originated from. - * - * @var string - */ - public $ip; - - /** - * The domain that was used to try to verify the request with recaptcha api. - * - * @var string - */ - public $domain; - /** * Create a new event instance. - * - * @param string $ip - * @param string $domain */ - public function __construct($ip, $domain) + public function __construct(public string $ip, public string $domain) { - $this->ip = $ip; - $this->domain = $domain; } } diff --git a/app/Events/Auth/FailedPasswordReset.php b/app/Events/Auth/FailedPasswordReset.php index 913bd0a6f..7a2bc3799 100644 --- a/app/Events/Auth/FailedPasswordReset.php +++ b/app/Events/Auth/FailedPasswordReset.php @@ -1,43 +1,18 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Auth; +use Pterodactyl\Events\Event; use Illuminate\Queue\SerializesModels; -class FailedPasswordReset +class FailedPasswordReset extends Event { use SerializesModels; - /** - * The IP that the request originated from. - * - * @var string - */ - public $ip; - - /** - * The email address that was used when the reset request failed. - * - * @var string - */ - public $email; - /** * Create a new event instance. - * - * @param string $ip - * @param string $email */ - public function __construct($ip, $email) + public function __construct(public string $ip, public string $email) { - $this->ip = $ip; - $this->email = $email; } } diff --git a/app/Events/Auth/ProvidedAuthenticationToken.php b/app/Events/Auth/ProvidedAuthenticationToken.php new file mode 100644 index 000000000..baf363d52 --- /dev/null +++ b/app/Events/Auth/ProvidedAuthenticationToken.php @@ -0,0 +1,13 @@ +. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Created +class Created extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Creating.php b/app/Events/Server/Creating.php index 318156b6d..36e4c5ccb 100644 --- a/app/Events/Server/Creating.php +++ b/app/Events/Server/Creating.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Creating +class Creating extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Deleted.php b/app/Events/Server/Deleted.php index 258ac75b1..04e496185 100644 --- a/app/Events/Server/Deleted.php +++ b/app/Events/Server/Deleted.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Deleted +class Deleted extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Deleting.php b/app/Events/Server/Deleting.php index 49219afc9..636b9483c 100644 --- a/app/Events/Server/Deleting.php +++ b/app/Events/Server/Deleting.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Deleting +class Deleting extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Installed.php b/app/Events/Server/Installed.php index 81b6963bc..614ead218 100644 --- a/app/Events/Server/Installed.php +++ b/app/Events/Server/Installed.php @@ -10,18 +10,10 @@ class Installed extends Event { use SerializesModels; - /** - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @var \Pterodactyl\Models\Server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Saved.php b/app/Events/Server/Saved.php index 4b8ae2165..d9b4151eb 100644 --- a/app/Events/Server/Saved.php +++ b/app/Events/Server/Saved.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Saved +class Saved extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Saving.php b/app/Events/Server/Saving.php index 9bb8153fe..f2ee59dfa 100644 --- a/app/Events/Server/Saving.php +++ b/app/Events/Server/Saving.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Saving +class Saving extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Updated.php b/app/Events/Server/Updated.php index 7157d4b34..35677f35d 100644 --- a/app/Events/Server/Updated.php +++ b/app/Events/Server/Updated.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Updated +class Updated extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Server/Updating.php b/app/Events/Server/Updating.php index c91c8a122..5e70e3add 100644 --- a/app/Events/Server/Updating.php +++ b/app/Events/Server/Updating.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Server; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Server; use Illuminate\Queue\SerializesModels; -class Updating +class Updating extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Server - */ - public $server; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Server $server */ - public function __construct(Server $server) + public function __construct(public Server $server) { - $this->server = $server; } } diff --git a/app/Events/Subuser/Created.php b/app/Events/Subuser/Created.php index 63941b2af..bd4cdbe67 100644 --- a/app/Events/Subuser/Created.php +++ b/app/Events/Subuser/Created.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Subuser; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Subuser; use Illuminate\Queue\SerializesModels; -class Created +class Created extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Subuser - */ - public $subuser; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function __construct(Subuser $subuser) + public function __construct(public Subuser $subuser) { - $this->subuser = $subuser; } } diff --git a/app/Events/Subuser/Creating.php b/app/Events/Subuser/Creating.php index 5d6f6d800..fd1e00eea 100644 --- a/app/Events/Subuser/Creating.php +++ b/app/Events/Subuser/Creating.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Subuser; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Subuser; use Illuminate\Queue\SerializesModels; -class Creating +class Creating extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Subuser - */ - public $subuser; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function __construct(Subuser $subuser) + public function __construct(public Subuser $subuser) { - $this->subuser = $subuser; } } diff --git a/app/Events/Subuser/Deleted.php b/app/Events/Subuser/Deleted.php index ee53388b7..6c0dc05ec 100644 --- a/app/Events/Subuser/Deleted.php +++ b/app/Events/Subuser/Deleted.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Subuser; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Subuser; use Illuminate\Queue\SerializesModels; -class Deleted +class Deleted extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Subuser - */ - public $subuser; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function __construct(Subuser $subuser) + public function __construct(public Subuser $subuser) { - $this->subuser = $subuser; } } diff --git a/app/Events/Subuser/Deleting.php b/app/Events/Subuser/Deleting.php index 6d25a3ebb..d26b3fc44 100644 --- a/app/Events/Subuser/Deleting.php +++ b/app/Events/Subuser/Deleting.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\Subuser; +use Pterodactyl\Events\Event; use Pterodactyl\Models\Subuser; use Illuminate\Queue\SerializesModels; -class Deleting +class Deleting extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\Subuser - */ - public $subuser; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function __construct(Subuser $subuser) + public function __construct(public Subuser $subuser) { - $this->subuser = $subuser; } } diff --git a/app/Events/User/Created.php b/app/Events/User/Created.php index b66d531d4..b4e3cf792 100644 --- a/app/Events/User/Created.php +++ b/app/Events/User/Created.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\User; use Pterodactyl\Models\User; +use Pterodactyl\Events\Event; use Illuminate\Queue\SerializesModels; -class Created +class Created extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\User - */ - public $user; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\User $user */ - public function __construct(User $user) + public function __construct(public User $user) { - $this->user = $user; } } diff --git a/app/Events/User/Creating.php b/app/Events/User/Creating.php index d6297440b..75f76c95b 100644 --- a/app/Events/User/Creating.php +++ b/app/Events/User/Creating.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\User; use Pterodactyl\Models\User; +use Pterodactyl\Events\Event; use Illuminate\Queue\SerializesModels; -class Creating +class Creating extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\User - */ - public $user; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\User $user */ - public function __construct(User $user) + public function __construct(public User $user) { - $this->user = $user; } } diff --git a/app/Events/User/Deleted.php b/app/Events/User/Deleted.php index 1b1682ef0..914014cf5 100644 --- a/app/Events/User/Deleted.php +++ b/app/Events/User/Deleted.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\User; use Pterodactyl\Models\User; +use Pterodactyl\Events\Event; use Illuminate\Queue\SerializesModels; -class Deleted +class Deleted extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\User - */ - public $user; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\User $user */ - public function __construct(User $user) + public function __construct(public User $user) { - $this->user = $user; } } diff --git a/app/Events/User/Deleting.php b/app/Events/User/Deleting.php index 05e3ba252..ba8df8b74 100644 --- a/app/Events/User/Deleting.php +++ b/app/Events/User/Deleting.php @@ -1,35 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Events\User; use Pterodactyl\Models\User; +use Pterodactyl\Events\Event; use Illuminate\Queue\SerializesModels; -class Deleting +class Deleting extends Event { use SerializesModels; - /** - * The Eloquent model of the server. - * - * @var \Pterodactyl\Models\User - */ - public $user; - /** * Create a new event instance. - * - * @param \Pterodactyl\Models\User $user */ - public function __construct(User $user) + public function __construct(public User $user) { - $this->user = $user; } } diff --git a/app/Exceptions/AccountNotFoundException.php b/app/Exceptions/AccountNotFoundException.php index f9430eeb7..3da3de250 100644 --- a/app/Exceptions/AccountNotFoundException.php +++ b/app/Exceptions/AccountNotFoundException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions; diff --git a/app/Exceptions/AutoDeploymentException.php b/app/Exceptions/AutoDeploymentException.php index d2455318f..20405fba2 100644 --- a/app/Exceptions/AutoDeploymentException.php +++ b/app/Exceptions/AutoDeploymentException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions; diff --git a/app/Exceptions/DisplayException.php b/app/Exceptions/DisplayException.php index a00939711..5ea2140d1 100644 --- a/app/Exceptions/DisplayException.php +++ b/app/Exceptions/DisplayException.php @@ -3,73 +3,55 @@ namespace Pterodactyl\Exceptions; use Exception; -use Throwable; +use Illuminate\Http\Request; use Psr\Log\LoggerInterface; use Illuminate\Http\Response; +use Illuminate\Http\JsonResponse; use Illuminate\Container\Container; -use Prologue\Alerts\AlertsMessageBag; +use Illuminate\Http\RedirectResponse; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; -class DisplayException extends PterodactylException +class DisplayException extends PterodactylException implements HttpExceptionInterface { - const LEVEL_DEBUG = 'debug'; - const LEVEL_INFO = 'info'; - const LEVEL_WARNING = 'warning'; - const LEVEL_ERROR = 'error'; + public const LEVEL_DEBUG = 'debug'; + public const LEVEL_INFO = 'info'; + public const LEVEL_WARNING = 'warning'; + public const LEVEL_ERROR = 'error'; /** - * @var string + * DisplayException constructor. */ - protected $level; - - /** - * Exception constructor. - * - * @param string $message - * @param Throwable|null $previous - * @param string $level - * @param int $code - */ - public function __construct($message, Throwable $previous = null, $level = self::LEVEL_ERROR, $code = 0) + public function __construct(string $message, ?\Throwable $previous = null, protected string $level = self::LEVEL_ERROR, int $code = 0) { parent::__construct($message, $code, $previous); - - $this->level = $level; } - /** - * @return string - */ - public function getErrorLevel() + public function getErrorLevel(): string { return $this->level; } - /** - * @return int - */ - public function getStatusCode() + public function getStatusCode(): int { return Response::HTTP_BAD_REQUEST; } + public function getHeaders(): array + { + return []; + } + /** * Render the exception to the user by adding a flashed message to the session * and then redirecting them back to the page that they came from. If the * request originated from an API hit, return the error in JSONAPI spec format. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse */ - public function render($request) + public function render(Request $request): JsonResponse|RedirectResponse { if ($request->expectsJson()) { - return response()->json(Handler::convertToArray($this, [ - 'detail' => $this->getMessage(), - ]), method_exists($this, 'getStatusCode') ? $this->getStatusCode() : Response::HTTP_BAD_REQUEST); + return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders()); } - Container::getInstance()->make(AlertsMessageBag::class)->danger($this->getMessage())->flash(); - return redirect()->back()->withInput(); } @@ -77,19 +59,17 @@ class DisplayException extends PterodactylException * Log the exception to the logs using the defined error level only if the previous * exception is set. * - * @return mixed - * - * @throws \Exception + * @throws \Throwable */ public function report() { - if (! $this->getPrevious() instanceof Exception || ! Handler::isReportable($this->getPrevious())) { + if (!$this->getPrevious() instanceof \Exception || !Handler::isReportable($this->getPrevious())) { return null; } try { $logger = Container::getInstance()->make(LoggerInterface::class); - } catch (Exception $ex) { + } catch (Exception) { throw $this->getPrevious(); } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 14cef0764..5786d7dd5 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -3,24 +3,30 @@ namespace Pterodactyl\Exceptions; use Exception; -use PDOException; -use Psr\Log\LoggerInterface; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; +use Illuminate\Http\JsonResponse; +use Illuminate\Support\Collection; use Illuminate\Container\Container; use Illuminate\Database\Connection; +use Illuminate\Http\RedirectResponse; +use Illuminate\Foundation\Application; use Illuminate\Auth\AuthenticationException; use Illuminate\Session\TokenMismatchException; use Illuminate\Validation\ValidationException; +use Symfony\Component\HttpFoundation\Response; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Mailer\Exception\TransportException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; -class Handler extends ExceptionHandler +final class Handler extends ExceptionHandler { /** - * Laravel's validation parser formats custom rules using the class name + * The validation parser in Laravel formats custom rules using the class name * resulting in some weird rule names. This string will be parsed out and * replaced with 'p_' in the response code. */ @@ -28,8 +34,6 @@ class Handler extends ExceptionHandler /** * A list of the exception types that should not be reported. - * - * @var array */ protected $dontReport = [ AuthenticationException::class, @@ -42,19 +46,17 @@ class Handler extends ExceptionHandler ]; /** - * A list of exceptions that should be logged with cleaned stack - * traces to avoid exposing credentials or other sensitive information. - * - * @var array + * Maps exceptions to a specific response code. This handles special exception + * types that don't have a defined response code. */ - protected $cleanStacks = [ - PDOException::class, + protected static array $exceptionResponseCodes = [ + AuthenticationException::class => 401, + AuthorizationException::class => 403, + ValidationException::class => 422, ]; /** * A list of the inputs that are never flashed for validation exceptions. - * - * @var array */ protected $dontFlash = [ 'token', @@ -64,56 +66,40 @@ class Handler extends ExceptionHandler ]; /** - * Report or log an exception. Skips Laravel's internal reporter since we - * don't need or want the user information in our logs by default. + * Registers the exception handling callbacks for the application. This + * will capture specific exception types that we do not want to include + * the detailed stack traces for since they could reveal credentials to + * whoever can read the logs. * - * If you want to implement logging in a different format to integrate with - * services such as AWS Cloudwatch or other monitoring you can replace the - * contents of this function with a call to the parent reporter. - * - * @param \Exception $exception - * @return mixed - * - * @throws \Exception + * @noinspection PhpUnusedLocalVariableInspection */ - public function report(Exception $exception) + public function register(): void { - if (! config('app.exceptions.report_all', false) && $this->shouldntReport($exception)) { - return null; + if (config('app.exceptions.report_all', false)) { + $this->dontReport = []; } - if (method_exists($exception, 'report')) { - return $exception->report(); - } + $this->reportable(function (\PDOException $ex) { + $ex = $this->generateCleanedExceptionStack($ex); + }); - try { - $logger = $this->container->make(LoggerInterface::class); - } catch (Exception $ex) { - throw $exception; - } - - foreach ($this->cleanStacks as $class) { - if ($exception instanceof $class) { - $exception = $this->generateCleanedExceptionStack($exception); - break; - } - } - - return $logger->error($exception); + $this->reportable(function (TransportException $ex) { + $ex = $this->generateCleanedExceptionStack($ex); + }); } - private function generateCleanedExceptionStack(Exception $exception) + private function generateCleanedExceptionStack(\Throwable $exception): string { $cleanedStack = ''; foreach ($exception->getTrace() as $index => $item) { $cleanedStack .= sprintf( "#%d %s(%d): %s%s%s\n", $index, - array_get($item, 'file'), - array_get($item, 'line'), - array_get($item, 'class'), - array_get($item, 'type'), - array_get($item, 'function') + Arr::get($item, 'file'), + Arr::get($item, 'line'), + Arr::get($item, 'class'), + Arr::get($item, 'type'), + Arr::get($item, 'function') ); } @@ -132,14 +118,12 @@ class Handler extends ExceptionHandler * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request - * @param \Exception $exception - * @return \Symfony\Component\HttpFoundation\Response * - * @throws \Exception + * @throws \Throwable */ - public function render($request, Exception $exception) + public function render($request, \Throwable $e): Response { - $connections = Container::getInstance()->make(Connection::class); + $connections = $this->container->make(Connection::class); // If we are currently wrapped up inside a transaction, we will roll all the way // back to the beginning. This needs to happen, otherwise session data does not @@ -154,58 +138,42 @@ class Handler extends ExceptionHandler $connections->rollBack(0); } - // Because of some breaking change snuck into a Laravel update that didn't get caught - // by any of the tests, exceptions implementing the HttpExceptionInterface get marked - // as being HttpExceptions, but aren't actually implementing the HttpException abstract. - // - // This is incredibly annoying because we can't just temporarily override the handler to - // allow these (at least without taking on a high maintenance cost). Laravel 5.8 fixes this, - // so when we update (or have updated) this code can be removed. - // - // @see https://github.com/laravel/framework/pull/25975 - // @todo remove this code when upgrading to Laravel 5.8 - if ($exception instanceof HttpExceptionInterface && ! $exception instanceof HttpException) { - $exception = new HttpException( - $exception->getStatusCode(), - $exception->getMessage(), - $exception, - $exception->getHeaders(), - $exception->getCode() - ); - } - - return parent::render($request, $exception); + return parent::render($request, $e); } /** * Transform a validation exception into a consistent format to be returned for * calls to the API. * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Validation\ValidationException $exception - * @return \Illuminate\Http\JsonResponse + * @param \Illuminate\Http\Request $request */ - public function invalidJson($request, ValidationException $exception) + public function invalidJson($request, ValidationException $exception): JsonResponse { - $codes = collect($exception->validator->failed())->mapWithKeys(function ($reasons, $field) { + $codes = Collection::make($exception->validator->failed())->mapWithKeys(function ($reasons, $field) { $cleaned = []; foreach ($reasons as $reason => $attrs) { - $cleaned[] = snake_case($reason); + $cleaned[] = Str::snake($reason); } return [str_replace('.', '_', $field) => $cleaned]; })->toArray(); - $errors = collect($exception->errors())->map(function ($errors, $field) use ($codes) { + $errors = Collection::make($exception->errors())->map(function ($errors, $field) use ($codes, $exception) { $response = []; foreach ($errors as $key => $error) { - $response[] = [ - 'code' => str_replace(self::PTERODACTYL_RULE_STRING, 'p_', array_get( - $codes, str_replace('.', '_', $field) . '.' . $key + $meta = [ + 'source_field' => $field, + 'rule' => str_replace(self::PTERODACTYL_RULE_STRING, 'p_', Arr::get( + $codes, + str_replace('.', '_', $field) . '.' . $key )), - 'detail' => $error, - 'source' => ['field' => $field], ]; + + $converted = $this->convertExceptionToArray($exception)['errors'][0]; + $converted['detail'] = $error; + $converted['meta'] = array_merge($converted['meta'] ?? [], $meta); + + $response[] = $converted; } return $response; @@ -218,28 +186,43 @@ class Handler extends ExceptionHandler /** * Return the exception as a JSONAPI representation for use on API requests. - * - * @param \Exception $exception - * @param array $override - * @return array */ - public static function convertToArray(Exception $exception, array $override = []): array + protected function convertExceptionToArray(\Throwable $e, array $override = []): array { + $match = self::$exceptionResponseCodes[get_class($e)] ?? null; + $error = [ - 'code' => class_basename($exception), - 'status' => method_exists($exception, 'getStatusCode') ? strval($exception->getStatusCode()) : '500', - 'detail' => 'An error was encountered while processing this request.', + 'code' => class_basename($e), + 'status' => method_exists($e, 'getStatusCode') + ? strval($e->getStatusCode()) + : strval($match ?? '500'), + 'detail' => $e instanceof HttpExceptionInterface || !is_null($match) + ? $e->getMessage() + : 'An unexpected error was encountered while processing this request, please try again.', ]; + if ($e instanceof ModelNotFoundException || $e->getPrevious() instanceof ModelNotFoundException) { + // Show a nicer error message compared to the standard "No query results for model" + // response that is normally returned. If we are in debug mode this will get overwritten + // with a more specific error message to help narrow down things. + $error['detail'] = 'The requested resource could not be found on the server.'; + } + if (config('app.debug')) { $error = array_merge($error, [ - 'detail' => $exception->getMessage(), + 'detail' => $e->getMessage(), 'source' => [ - 'line' => $exception->getLine(), - 'file' => str_replace(base_path(), '', $exception->getFile()), + 'line' => $e->getLine(), + 'file' => str_replace(Application::getInstance()->basePath(), '', $e->getFile()), ], 'meta' => [ - 'trace' => explode("\n", $exception->getTraceAsString()), + 'trace' => Collection::make($e->getTrace()) + ->map(fn ($trace) => Arr::except($trace, ['args'])) + ->all(), + 'previous' => Collection::make($this->extractPrevious($e)) + ->map(fn ($exception) => $e->getTrace()) + ->map(fn ($trace) => Arr::except($trace, ['args'])) + ->all(), ], ]); } @@ -249,11 +232,8 @@ class Handler extends ExceptionHandler /** * Return an array of exceptions that should not be reported. - * - * @param \Exception $exception - * @return bool */ - public static function isReportable(Exception $exception): bool + public static function isReportable(\Exception $exception): bool { return (new static(Container::getInstance()))->shouldReport($exception); } @@ -261,28 +241,43 @@ class Handler extends ExceptionHandler /** * Convert an authentication exception into an unauthenticated response. * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Auth\AuthenticationException $exception - * @return \Illuminate\Http\Response + * @param \Illuminate\Http\Request $request */ - protected function unauthenticated($request, AuthenticationException $exception) + protected function unauthenticated($request, AuthenticationException $exception): JsonResponse|RedirectResponse { if ($request->expectsJson()) { - return response()->json(['error' => 'Unauthenticated.'], 401); + return new JsonResponse($this->convertExceptionToArray($exception), JsonResponse::HTTP_UNAUTHORIZED); } - return redirect()->guest(route('auth.login')); + return redirect()->guest('/auth/login'); } /** - * Converts an exception into an array to render in the response. Overrides - * Laravel's built-in converter to output as a JSONAPI spec compliant object. + * Extracts all the previous exceptions that lead to the one passed into this + * function being thrown. * - * @param \Exception $exception - * @return array + * @return \Throwable[] */ - protected function convertExceptionToArray(Exception $exception) + protected function extractPrevious(\Throwable $e): array { - return self::convertToArray($exception); + $previous = []; + while ($value = $e->getPrevious()) { + if (!$value instanceof \Throwable) { + break; + } + $previous[] = $value; + $e = $value; + } + + return $previous; + } + + /** + * Helper method to allow reaching into the handler to convert an exception + * into the expected array response type. + */ + public static function toArray(\Throwable $e): array + { + return (new self(app()))->convertExceptionToArray($e); } } diff --git a/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php b/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php index 5f17186bc..bdcb8ec34 100644 --- a/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php +++ b/app/Exceptions/Http/Base/InvalidPasswordProvidedException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Http\Base; diff --git a/app/Exceptions/Http/Connection/DaemonConnectionException.php b/app/Exceptions/Http/Connection/DaemonConnectionException.php index f2892789c..8c3d474a6 100644 --- a/app/Exceptions/Http/Connection/DaemonConnectionException.php +++ b/app/Exceptions/Http/Connection/DaemonConnectionException.php @@ -3,43 +3,88 @@ namespace Pterodactyl\Exceptions\Http\Connection; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Log; use GuzzleHttp\Exception\GuzzleException; use Pterodactyl\Exceptions\DisplayException; +/** + * @method \GuzzleHttp\Exception\GuzzleException getPrevious() + */ class DaemonConnectionException extends DisplayException { + private int $statusCode = Response::HTTP_GATEWAY_TIMEOUT; + /** - * @var int + * Every request to the Wings instance will return a unique X-Request-Id header + * which allows for all errors to be efficiently tied to a specific request that + * triggered them, and gives users a more direct method of informing hosts when + * something goes wrong. */ - private $statusCode = Response::HTTP_GATEWAY_TIMEOUT; + private ?string $requestId; /** * Throw a displayable exception caused by a daemon connection error. - * - * @param \GuzzleHttp\Exception\GuzzleException $previous - * @param bool $useStatusCode */ - public function __construct(GuzzleException $previous, bool $useStatusCode = false) + public function __construct(GuzzleException $previous, bool $useStatusCode = true) { /** @var \GuzzleHttp\Psr7\Response|null $response */ $response = method_exists($previous, 'getResponse') ? $previous->getResponse() : null; + $this->requestId = $response?->getHeaderLine('X-Request-Id'); if ($useStatusCode) { - $this->statusCode = is_null($response) ? 500 : $response->getStatusCode(); + $this->statusCode = is_null($response) ? $this->statusCode : $response->getStatusCode(); + // There are rare conditions where wings encounters a panic condition and crashes the + // request being made after content has already been sent over the wire. In these cases + // you can end up with a "successful" response code that is actual an error. + // + // Handle those better here since we shouldn't ever end up in this exception state and + // be returning a 2XX level response. + if ($this->statusCode < 400) { + $this->statusCode = Response::HTTP_BAD_GATEWAY; + } } - parent::__construct(trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ]), $previous, DisplayException::LEVEL_WARNING); + if (is_null($response)) { + $message = 'Could not establish a connection to the machine running this server. Please try again.'; + } else { + $message = sprintf('There was an error while communicating with the machine running this server. This error has been logged, please try again. (code: %s) (request_id: %s)', $response->getStatusCode(), $this->requestId ?? ''); + } + + // Attempt to pull the actual error message off the response and return that if it is not + // a 500 level error. + if ($this->statusCode < 500 && !is_null($response)) { + $body = json_decode($response->getBody()->__toString(), true); + $message = sprintf('An error occurred on the remote host: %s. (request id: %s)', $body['error'] ?? $message, $this->requestId ?? ''); + } + + $level = $this->statusCode >= 500 && $this->statusCode !== 504 + ? DisplayException::LEVEL_ERROR + : DisplayException::LEVEL_WARNING; + + parent::__construct($message, $previous, $level); + } + + /** + * Override the default reporting method for DisplayException by just logging immediately + * here and including the specific X-Request-Id header that was returned by the call. + */ + public function report() + { + Log::{$this->getErrorLevel()}($this->getPrevious(), [ + 'request_id' => $this->requestId, + ]); } /** * Return the HTTP status code for this exception. - * - * @return int */ - public function getStatusCode() + public function getStatusCode(): int { return $this->statusCode; } + + public function getRequestId(): ?string + { + return $this->requestId; + } } diff --git a/app/Exceptions/Http/HttpForbiddenException.php b/app/Exceptions/Http/HttpForbiddenException.php new file mode 100644 index 000000000..1488265a9 --- /dev/null +++ b/app/Exceptions/Http/HttpForbiddenException.php @@ -0,0 +1,17 @@ +. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Http\Server; @@ -13,4 +6,11 @@ use Pterodactyl\Exceptions\DisplayException; class FileSizeTooLargeException extends DisplayException { + /** + * FileSizeTooLargeException constructor. + */ + public function __construct() + { + parent::__construct('The file you are attempting to open is too large to view in the file editor.'); + } } diff --git a/app/Exceptions/Http/Server/FileTypeNotEditableException.php b/app/Exceptions/Http/Server/FileTypeNotEditableException.php index c47dd3695..06f6a508b 100644 --- a/app/Exceptions/Http/Server/FileTypeNotEditableException.php +++ b/app/Exceptions/Http/Server/FileTypeNotEditableException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Http\Server; diff --git a/app/Exceptions/Http/Server/ServerStateConflictException.php b/app/Exceptions/Http/Server/ServerStateConflictException.php new file mode 100644 index 000000000..546024c23 --- /dev/null +++ b/app/Exceptions/Http/Server/ServerStateConflictException.php @@ -0,0 +1,31 @@ +isSuspended()) { + $message = 'This server is currently suspended and the functionality requested is unavailable.'; + } elseif ($server->node->isUnderMaintenance()) { + $message = 'The node of this server is currently under maintenance and the functionality requested is unavailable.'; + } elseif (!$server->isInstalled()) { + $message = 'This server has not yet completed its installation process, please try again later.'; + } elseif ($server->status === Server::STATUS_RESTORING_BACKUP) { + $message = 'This server is currently restoring from a backup, please try again later.'; + } elseif (!is_null($server->transfer)) { + $message = 'This server is currently being transferred to a new machine, please try again later.'; + } + + parent::__construct($message, $previous); + } +} diff --git a/app/Exceptions/Http/TwoFactorAuthRequiredException.php b/app/Exceptions/Http/TwoFactorAuthRequiredException.php new file mode 100644 index 000000000..95f300e2e --- /dev/null +++ b/app/Exceptions/Http/TwoFactorAuthRequiredException.php @@ -0,0 +1,18 @@ +errors()->toJson() + $message = sprintf( + 'Could not save %s[%s]: failed to validate data: %s', + get_class($model), + $model->getKey(), + $validator->errors()->toJson() ); - $this->validator = $validator; + parent::__construct($message); } /** * Return the validator message bag. - * - * @return \Illuminate\Support\MessageBag */ - public function getMessageBag() + public function getMessageBag(): MessageBag { return $this->validator->errors(); } /** * Return the status code for this request. - * - * @return int */ - public function getStatusCode() + public function getStatusCode(): int { return 500; } - /** - * @return array - */ - public function getHeaders() + public function getHeaders(): array { return []; } + + public function getValidator(): Validator + { + return $this->validator; + } + + public function getModel(): Model + { + return $this->model; + } } diff --git a/app/Exceptions/Repository/RecordNotFoundException.php b/app/Exceptions/Repository/RecordNotFoundException.php index 53cfa2222..ed1127ce7 100644 --- a/app/Exceptions/Repository/RecordNotFoundException.php +++ b/app/Exceptions/Repository/RecordNotFoundException.php @@ -9,20 +9,16 @@ class RecordNotFoundException extends RepositoryException implements HttpExcepti { /** * Returns the status code. - * - * @return int */ - public function getStatusCode() + public function getStatusCode(): int { return Response::HTTP_NOT_FOUND; } /** * Returns response headers. - * - * @return array */ - public function getHeaders() + public function getHeaders(): array { return []; } diff --git a/app/Exceptions/Service/Allocation/AutoAllocationNotEnabledException.php b/app/Exceptions/Service/Allocation/AutoAllocationNotEnabledException.php new file mode 100644 index 000000000..a593347e6 --- /dev/null +++ b/app/Exceptions/Service/Allocation/AutoAllocationNotEnabledException.php @@ -0,0 +1,18 @@ + $port])); } diff --git a/app/Exceptions/Service/Allocation/NoAutoAllocationSpaceAvailableException.php b/app/Exceptions/Service/Allocation/NoAutoAllocationSpaceAvailableException.php new file mode 100644 index 000000000..8e3c9b0a1 --- /dev/null +++ b/app/Exceptions/Service/Allocation/NoAutoAllocationSpaceAvailableException.php @@ -0,0 +1,18 @@ +. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Egg; diff --git a/app/Exceptions/Service/Egg/InvalidCopyFromException.php b/app/Exceptions/Service/Egg/InvalidCopyFromException.php index 149c42dd6..d0e33e21f 100644 --- a/app/Exceptions/Service/Egg/InvalidCopyFromException.php +++ b/app/Exceptions/Service/Egg/InvalidCopyFromException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Egg; diff --git a/app/Exceptions/Service/Egg/NoParentConfigurationFoundException.php b/app/Exceptions/Service/Egg/NoParentConfigurationFoundException.php index 867b09c1a..ab9f5b404 100644 --- a/app/Exceptions/Service/Egg/NoParentConfigurationFoundException.php +++ b/app/Exceptions/Service/Egg/NoParentConfigurationFoundException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Egg; diff --git a/app/Exceptions/Service/Egg/Variable/ReservedVariableNameException.php b/app/Exceptions/Service/Egg/Variable/ReservedVariableNameException.php index 03ad09e5e..bc921ee25 100644 --- a/app/Exceptions/Service/Egg/Variable/ReservedVariableNameException.php +++ b/app/Exceptions/Service/Egg/Variable/ReservedVariableNameException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Egg\Variable; diff --git a/app/Exceptions/Service/HasActiveServersException.php b/app/Exceptions/Service/HasActiveServersException.php index 5c43101c5..d3b46a884 100644 --- a/app/Exceptions/Service/HasActiveServersException.php +++ b/app/Exceptions/Service/HasActiveServersException.php @@ -7,10 +7,7 @@ use Pterodactyl\Exceptions\DisplayException; class HasActiveServersException extends DisplayException { - /** - * @return int - */ - public function getStatusCode() + public function getStatusCode(): int { return Response::HTTP_BAD_REQUEST; } diff --git a/app/Exceptions/Service/Helper/CdnVersionFetchingException.php b/app/Exceptions/Service/Helper/CdnVersionFetchingException.php index d9a2cd7f5..e085d3635 100644 --- a/app/Exceptions/Service/Helper/CdnVersionFetchingException.php +++ b/app/Exceptions/Service/Helper/CdnVersionFetchingException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Helper; diff --git a/app/Exceptions/Service/InvalidFileUploadException.php b/app/Exceptions/Service/InvalidFileUploadException.php index 6d0585d2a..7d73ac207 100644 --- a/app/Exceptions/Service/InvalidFileUploadException.php +++ b/app/Exceptions/Service/InvalidFileUploadException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service; diff --git a/app/Exceptions/Service/Location/HasActiveNodesException.php b/app/Exceptions/Service/Location/HasActiveNodesException.php index 1270807b8..43a4e01e3 100644 --- a/app/Exceptions/Service/Location/HasActiveNodesException.php +++ b/app/Exceptions/Service/Location/HasActiveNodesException.php @@ -7,10 +7,7 @@ use Pterodactyl\Exceptions\DisplayException; class HasActiveNodesException extends DisplayException { - /** - * @return int - */ - public function getStatusCode() + public function getStatusCode(): int { return Response::HTTP_BAD_REQUEST; } diff --git a/app/Exceptions/Service/Pack/InvalidFileMimeTypeException.php b/app/Exceptions/Service/Pack/InvalidFileMimeTypeException.php deleted file mode 100644 index d0515ff43..000000000 --- a/app/Exceptions/Service/Pack/InvalidFileMimeTypeException.php +++ /dev/null @@ -1,16 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Exceptions\Service\Pack; - -use Pterodactyl\Exceptions\DisplayException; - -class InvalidFileMimeTypeException extends DisplayException -{ -} diff --git a/app/Exceptions/Service/Pack/InvalidPackArchiveFormatException.php b/app/Exceptions/Service/Pack/InvalidPackArchiveFormatException.php deleted file mode 100644 index 5e216fed4..000000000 --- a/app/Exceptions/Service/Pack/InvalidPackArchiveFormatException.php +++ /dev/null @@ -1,16 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Exceptions\Service\Pack; - -use Pterodactyl\Exceptions\DisplayException; - -class InvalidPackArchiveFormatException extends DisplayException -{ -} diff --git a/app/Exceptions/Service/Pack/UnreadableZipArchiveException.php b/app/Exceptions/Service/Pack/UnreadableZipArchiveException.php deleted file mode 100644 index f1608936c..000000000 --- a/app/Exceptions/Service/Pack/UnreadableZipArchiveException.php +++ /dev/null @@ -1,16 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Exceptions\Service\Pack; - -use Pterodactyl\Exceptions\DisplayException; - -class UnreadableZipArchiveException extends DisplayException -{ -} diff --git a/app/Exceptions/Service/Pack/ZipArchiveCreationException.php b/app/Exceptions/Service/Pack/ZipArchiveCreationException.php deleted file mode 100644 index 79caab261..000000000 --- a/app/Exceptions/Service/Pack/ZipArchiveCreationException.php +++ /dev/null @@ -1,14 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Exceptions\Service\Pack; - -class ZipArchiveCreationException extends \Exception -{ -} diff --git a/app/Exceptions/Service/Pack/ZipExtractionException.php b/app/Exceptions/Service/Pack/ZipExtractionException.php deleted file mode 100644 index 8a6a82c20..000000000 --- a/app/Exceptions/Service/Pack/ZipExtractionException.php +++ /dev/null @@ -1,16 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Exceptions\Service\Pack; - -use Pterodactyl\Exceptions\DisplayException; - -class ZipExtractionException extends DisplayException -{ -} diff --git a/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php b/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php index 1863ce7ee..0c3afb48c 100644 --- a/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php +++ b/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Schedule\Task; diff --git a/app/Exceptions/Service/Server/RequiredVariableMissingException.php b/app/Exceptions/Service/Server/RequiredVariableMissingException.php index 068c8af0f..e8423a428 100644 --- a/app/Exceptions/Service/Server/RequiredVariableMissingException.php +++ b/app/Exceptions/Service/Server/RequiredVariableMissingException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Server; diff --git a/app/Exceptions/Service/ServiceLimitExceededException.php b/app/Exceptions/Service/ServiceLimitExceededException.php new file mode 100644 index 000000000..450a1743b --- /dev/null +++ b/app/Exceptions/Service/ServiceLimitExceededException.php @@ -0,0 +1,17 @@ +. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Subuser; diff --git a/app/Exceptions/Service/Subuser/UserIsServerOwnerException.php b/app/Exceptions/Service/Subuser/UserIsServerOwnerException.php index 99839424e..cc7225a2b 100644 --- a/app/Exceptions/Service/Subuser/UserIsServerOwnerException.php +++ b/app/Exceptions/Service/Subuser/UserIsServerOwnerException.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\Subuser; diff --git a/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php b/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php index fdd0ec67f..b7b819b44 100644 --- a/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php +++ b/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php @@ -1,14 +1,16 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Exceptions\Service\User; -class TwoFactorAuthenticationTokenInvalid extends \Exception +use Pterodactyl\Exceptions\DisplayException; + +class TwoFactorAuthenticationTokenInvalid extends DisplayException { + /** + * TwoFactorAuthenticationTokenInvalid constructor. + */ + public function __construct() + { + parent::__construct('The provided two-factor authentication token was not valid.'); + } } diff --git a/app/Exceptions/Transformer/InvalidTransformerLevelException.php b/app/Exceptions/Transformer/InvalidTransformerLevelException.php deleted file mode 100644 index 3d4c24248..000000000 --- a/app/Exceptions/Transformer/InvalidTransformerLevelException.php +++ /dev/null @@ -1,9 +0,0 @@ -config = $app->make(ConfigRepository::class); + } + + /** + * Returns a backup adapter instance. + */ + public function adapter(string $name = null): FilesystemAdapter + { + return $this->get($name ?: $this->getDefaultAdapter()); + } + + /** + * Set the given backup adapter instance. + */ + public function set(string $name, FilesystemAdapter $disk): self + { + $this->adapters[$name] = $disk; + + return $this; + } + + /** + * Gets a backup adapter. + */ + protected function get(string $name): FilesystemAdapter + { + return $this->adapters[$name] = $this->resolve($name); + } + + /** + * Resolve the given backup disk. + */ + protected function resolve(string $name): FilesystemAdapter + { + $config = $this->getConfig($name); + + if (empty($config['adapter'])) { + throw new \InvalidArgumentException("Backup disk [$name] does not have a configured adapter."); + } + + $adapter = $config['adapter']; + + if (isset($this->customCreators[$name])) { + return $this->callCustomCreator($config); + } + + $adapterMethod = 'create' . Str::studly($adapter) . 'Adapter'; + if (method_exists($this, $adapterMethod)) { + $instance = $this->{$adapterMethod}($config); + + Assert::isInstanceOf($instance, FilesystemAdapter::class); + + return $instance; + } + + throw new \InvalidArgumentException("Adapter [$adapter] is not supported."); + } + + /** + * Calls a custom creator for a given adapter type. + */ + protected function callCustomCreator(array $config): mixed + { + return $this->customCreators[$config['adapter']]($this->app, $config); + } + + /** + * Creates a new Wings adapter. + */ + public function createWingsAdapter(array $config): FilesystemAdapter + { + return new InMemoryFilesystemAdapter(); + } + + /** + * Creates a new S3 adapter. + */ + public function createS3Adapter(array $config): FilesystemAdapter + { + $config['version'] = 'latest'; + + if (!empty($config['key']) && !empty($config['secret'])) { + $config['credentials'] = Arr::only($config, ['key', 'secret', 'token']); + } + + $client = new S3Client($config); + + return new S3Filesystem($client, $config['bucket'], $config['prefix'] ?? '', $config['options'] ?? []); + } + + /** + * Returns the configuration associated with a given backup type. + */ + protected function getConfig(string $name): array + { + return $this->config->get("backups.disks.$name") ?: []; + } + + /** + * Get the default backup driver name. + */ + public function getDefaultAdapter(): string + { + return $this->config->get('backups.default'); + } + + /** + * Set the default session driver name. + */ + public function setDefaultAdapter(string $name): void + { + $this->config->set('backups.default', $name); + } + + /** + * Unset the given adapter instances. + * + * @param string|string[] $adapter + */ + public function forget(array|string $adapter): self + { + foreach ((array) $adapter as $adapterName) { + unset($this->adapters[$adapterName]); + } + + return $this; + } + + /** + * Register a custom adapter creator closure. + */ + public function extend(string $adapter, \Closure $callback): self + { + $this->customCreators[$adapter] = $callback; + + return $this; + } +} diff --git a/app/Extensions/DynamicDatabaseConnection.php b/app/Extensions/DynamicDatabaseConnection.php index 670e75e44..5adbd171d 100644 --- a/app/Extensions/DynamicDatabaseConnection.php +++ b/app/Extensions/DynamicDatabaseConnection.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Extensions; @@ -16,54 +9,28 @@ use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; class DynamicDatabaseConnection { - const DB_CHARSET = 'utf8'; - const DB_COLLATION = 'utf8_unicode_ci'; - const DB_DRIVER = 'mysql'; - - /** - * @var \Illuminate\Config\Repository - */ - protected $config; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - protected $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface - */ - protected $repository; + public const DB_CHARSET = 'utf8'; + public const DB_COLLATION = 'utf8_unicode_ci'; + public const DB_DRIVER = 'mysql'; /** * DynamicDatabaseConnection constructor. - * - * @param \Illuminate\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter */ public function __construct( - ConfigRepository $config, - DatabaseHostRepositoryInterface $repository, - Encrypter $encrypter + protected ConfigRepository $config, + protected Encrypter $encrypter, + protected DatabaseHostRepositoryInterface $repository ) { - $this->config = $config; - $this->encrypter = $encrypter; - $this->repository = $repository; } /** * Adds a dynamic database connection entry to the runtime config. * - * @param string $connection - * @param \Pterodactyl\Models\DatabaseHost|int $host - * @param string $database - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function set($connection, $host, $database = 'mysql') + public function set(string $connection, DatabaseHost|int $host, string $database = 'mysql'): void { - if (! $host instanceof DatabaseHost) { + if (!$host instanceof DatabaseHost) { $host = $this->repository->find($host); } diff --git a/app/Extensions/Filesystem/S3Filesystem.php b/app/Extensions/Filesystem/S3Filesystem.php new file mode 100644 index 000000000..df57a508b --- /dev/null +++ b/app/Extensions/Filesystem/S3Filesystem.php @@ -0,0 +1,35 @@ +client; + } + + public function getBucket(): string + { + return $this->bucket; + } +} diff --git a/app/Extensions/Hashids.php b/app/Extensions/Hashids.php index 60e5d956b..1a6bfdb39 100644 --- a/app/Extensions/Hashids.php +++ b/app/Extensions/Hashids.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Extensions; @@ -17,10 +10,10 @@ class Hashids extends VendorHashids implements HashidsInterface /** * {@inheritdoc} */ - public function decodeFirst($encoded, $default = null) + public function decodeFirst(string $encoded, string $default = null): mixed { $result = $this->decode($encoded); - if (! is_array($result)) { + if (!is_array($result)) { return $default; } diff --git a/app/Extensions/Illuminate/Database/Eloquent/Builder.php b/app/Extensions/Illuminate/Database/Eloquent/Builder.php new file mode 100644 index 000000000..6ce63c2b6 --- /dev/null +++ b/app/Extensions/Illuminate/Database/Eloquent/Builder.php @@ -0,0 +1,16 @@ +accessToken = $accessToken; + $this->plainTextToken = $plainTextToken; + } +} diff --git a/app/Extensions/Lcobucci/JWT/Encoding/TimestampDates.php b/app/Extensions/Lcobucci/JWT/Encoding/TimestampDates.php new file mode 100644 index 000000000..e47a3fda0 --- /dev/null +++ b/app/Extensions/Lcobucci/JWT/Encoding/TimestampDates.php @@ -0,0 +1,29 @@ +getTimestamp(); + } + + return $claims; + } +} diff --git a/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php b/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php index 9599ca2c9..27b4724e1 100644 --- a/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php +++ b/app/Extensions/League/Fractal/Serializers/PterodactylSerializer.php @@ -6,29 +6,10 @@ use League\Fractal\Serializer\ArraySerializer; class PterodactylSerializer extends ArraySerializer { - /** - * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * @return array - */ - public function item($resourceKey, array $data) - { - return [ - 'object' => $resourceKey, - 'attributes' => $data, - ]; - } - /** * Serialize a collection. - * - * @param string $resourceKey - * @param array $data - * @return array */ - public function collection($resourceKey, array $data) + public function collection(?string $resourceKey, array $data): array { $response = []; foreach ($data as $datum) { @@ -42,11 +23,20 @@ class PterodactylSerializer extends ArraySerializer } /** - * Serialize a null resource. - * - * @return array + * Serialize an item. */ - public function null() + public function item(?string $resourceKey, array $data): array + { + return [ + 'object' => $resourceKey, + 'attributes' => $data, + ]; + } + + /** + * Serialize a null resource. + */ + public function null(): ?array { return [ 'object' => 'null_resource', @@ -56,12 +46,8 @@ class PterodactylSerializer extends ArraySerializer /** * Merge the included resources with the parent resource being serialized. - * - * @param array $transformedData - * @param array $includedData - * @return array */ - public function mergeIncludes($transformedData, $includedData) + public function mergeIncludes(array $transformedData, array $includedData): array { foreach ($includedData as $key => $datum) { $transformedData['relationships'][$key] = $datum; diff --git a/app/Extensions/Spatie/Fractalistic/Fractal.php b/app/Extensions/Spatie/Fractalistic/Fractal.php index 5bb0dd52b..7af9b5436 100644 --- a/app/Extensions/Spatie/Fractalistic/Fractal.php +++ b/app/Extensions/Spatie/Fractalistic/Fractal.php @@ -2,8 +2,9 @@ namespace Pterodactyl\Extensions\Spatie\Fractalistic; -use League\Fractal\TransformerAbstract; +use League\Fractal\Scope; use Spatie\Fractal\Fractal as SpatieFractal; +use Pterodactyl\Transformers\Api\Transformer; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use Pterodactyl\Extensions\League\Fractal\Serializers\PterodactylSerializer; @@ -13,16 +14,14 @@ class Fractal extends SpatieFractal /** * Create fractal data. * - * @return \League\Fractal\Scope - * * @throws \Spatie\Fractalistic\Exceptions\InvalidTransformation * @throws \Spatie\Fractalistic\Exceptions\NoTransformerSpecified */ - public function createData() + public function createData(): Scope { // Set the serializer by default. if (is_null($this->serializer)) { - $this->serializer = new PterodactylSerializer; + $this->serializer = new PterodactylSerializer(); } // Automatically set the paginator on the response object if the @@ -33,12 +32,9 @@ class Fractal extends SpatieFractal // If the resource name is not set attempt to pull it off the transformer // itself and set it automatically. - if ( - is_null($this->resourceName) - && $this->transformer instanceof TransformerAbstract - && method_exists($this->transformer, 'getResourceName') - ) { - $this->resourceName = $this->transformer->getResourceName(); + $class = is_string($this->transformer) ? new $this->transformer() : $this->transformer; + if (is_null($this->resourceName) && $class instanceof Transformer) { + $this->resourceName = $class->getResourceName(); } return parent::createData(); diff --git a/app/Facades/Activity.php b/app/Facades/Activity.php new file mode 100644 index 000000000..564021001 --- /dev/null +++ b/app/Facades/Activity.php @@ -0,0 +1,14 @@ +getTimezone()->toOffsetName(); + } +} diff --git a/app/Helpers/Utilities.php b/app/Helpers/Utilities.php index 5de685fe9..0c468e0c7 100644 --- a/app/Helpers/Utilities.php +++ b/app/Helpers/Utilities.php @@ -2,17 +2,16 @@ namespace Pterodactyl\Helpers; -use Exception; +use Carbon\Carbon; +use Cron\CronExpression; use Illuminate\Support\Facades\Log; +use Illuminate\Support\ViewErrorBag; class Utilities { /** * Generates a random string and injects special characters into it, in addition to - * the randomness of the alpha-numeric default response. - * - * @param int $length - * @return string + * the randomness of the alphanumeric default response. */ public static function randomStringWithSpecialCharacters(int $length = 16): string { @@ -20,16 +19,39 @@ class Utilities // Given a random string of characters, randomly loop through the characters and replace some // with special characters to avoid issues with MySQL password requirements on some servers. try { - for ($i = 0; $i < random_int(2, 6); $i++) { + for ($i = 0; $i < random_int(2, 6); ++$i) { $character = ['!', '@', '=', '.', '+', '^'][random_int(0, 5)]; $string = substr_replace($string, $character, random_int(0, $length - 1), 1); } - } catch (Exception $exception) { + } catch (\Exception $exception) { // Just log the error and hope for the best at this point. Log::error($exception); } return $string; } + + /** + * Converts schedule cron data into a carbon object. + * + * @throws \Exception + */ + public static function getScheduleNextRunDate(string $minute, string $hour, string $dayOfMonth, string $month, string $dayOfWeek): Carbon + { + return Carbon::instance((new CronExpression( + sprintf('%s %s %s %s %s', $minute, $hour, $dayOfMonth, $month, $dayOfWeek) + ))->getNextRunDate()); + } + + public static function checked(string $name, mixed $default): string + { + $errors = session('errors'); + + if (isset($errors) && $errors instanceof ViewErrorBag && $errors->any()) { + return old($name) ? 'checked' : ''; + } + + return ($default) ? 'checked' : ''; + } } diff --git a/app/Http/Controllers/Admin/ApiController.php b/app/Http/Controllers/Admin/ApiController.php deleted file mode 100644 index 2fa541bc7..000000000 --- a/app/Http/Controllers/Admin/ApiController.php +++ /dev/null @@ -1,118 +0,0 @@ -alert = $alert; - $this->keyCreationService = $keyCreationService; - $this->repository = $repository; - } - - /** - * Render view showing all of a user's application API keys. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request): View - { - return view('admin.api.index', [ - 'keys' => $this->repository->getApplicationKeys($request->user()), - ]); - } - - /** - * Render view allowing an admin to create a new application API key. - * - * @return \Illuminate\View\View - * @throws \ReflectionException - */ - public function create(): View - { - $resources = AdminAcl::getResourceList(); - sort($resources); - - return view('admin.api.new', [ - 'resources' => $resources, - 'permissions' => [ - 'r' => AdminAcl::READ, - 'rw' => AdminAcl::READ | AdminAcl::WRITE, - 'n' => AdminAcl::NONE, - ], - ]); - } - - /** - * Store the new key and redirect the user back to the application key listing. - * - * @param \Pterodactyl\Http\Requests\Admin\Api\StoreApplicationApiKeyRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function store(StoreApplicationApiKeyRequest $request): RedirectResponse - { - $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([ - 'memo' => $request->input('memo'), - 'user_id' => $request->user()->id, - ], $request->getKeyPermissions()); - - $this->alert->success('A new application API key has been generated for your account.')->flash(); - - return redirect()->route('admin.api.index'); - } - - /** - * Delete an application API key from the database. - * - * @param \Illuminate\Http\Request $request - * @param string $identifier - * @return \Illuminate\Http\Response - */ - public function delete(Request $request, string $identifier): Response - { - $this->repository->deleteApplicationKey($request->user(), $identifier); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Admin/BaseController.php b/app/Http/Controllers/Admin/BaseController.php index b80676f2e..dfaed2e36 100644 --- a/app/Http/Controllers/Admin/BaseController.php +++ b/app/Http/Controllers/Admin/BaseController.php @@ -4,32 +4,11 @@ namespace Pterodactyl\Http\Controllers\Admin; use Illuminate\View\View; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Helpers\SoftwareVersionService; class BaseController extends Controller { - /** - * @var \Pterodactyl\Services\Helpers\SoftwareVersionService - */ - private $version; - - /** - * BaseController constructor. - * - * @param \Pterodactyl\Services\Helpers\SoftwareVersionService $version - */ - public function __construct(SoftwareVersionService $version) - { - $this->version = $version; - } - - /** - * Return the admin index view. - * - * @return \Illuminate\View\View - */ public function index(): View { - return view('admin.index', ['version' => $this->version]); + return view('templates/base.core'); } } diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php deleted file mode 100644 index 4fecbbccf..000000000 --- a/app/Http/Controllers/Admin/DatabaseController.php +++ /dev/null @@ -1,192 +0,0 @@ -alert = $alert; - $this->creationService = $creationService; - $this->databaseRepository = $databaseRepository; - $this->deletionService = $deletionService; - $this->repository = $repository; - $this->locationRepository = $locationRepository; - $this->updateService = $updateService; - } - - /** - * Display database host index. - * - * @return \Illuminate\View\View - */ - public function index(): View - { - return view('admin.databases.index', [ - 'locations' => $this->locationRepository->getAllWithNodes(), - 'hosts' => $this->repository->getWithViewDetails(), - ]); - } - - /** - * Display database host to user. - * - * @param int $host - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function view(int $host): View - { - return view('admin.databases.view', [ - 'locations' => $this->locationRepository->getAllWithNodes(), - 'host' => $this->repository->find($host), - 'databases' => $this->databaseRepository->getDatabasesForHost($host), - ]); - } - - /** - * Handle request to create a new database host. - * - * @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Throwable - */ - public function create(DatabaseHostFormRequest $request): RedirectResponse - { - try { - $host = $this->creationService->handle($request->normalize()); - } catch (Exception $exception) { - if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) { - $this->alert->danger( - sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage()) - )->flash(); - - return redirect()->route('admin.databases')->withInput($request->validated()); - } else { - throw $exception; - } - } - - $this->alert->success('Successfully created a new database host on the system.')->flash(); - - return redirect()->route('admin.databases.view', $host->id); - } - - /** - * Handle updating database host. - * - * @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request - * @param \Pterodactyl\Models\DatabaseHost $host - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Throwable - */ - public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse - { - $redirect = redirect()->route('admin.databases.view', $host->id); - - try { - $this->updateService->handle($host->id, $request->normalize()); - $this->alert->success('Database host was updated successfully.')->flash(); - } catch (Exception $exception) { - // Catch any SQL related exceptions and display them back to the user, otherwise just - // throw the exception like normal and move on with it. - if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) { - $this->alert->danger( - sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage()) - )->flash(); - return $redirect->withInput($request->normalize()); - } else { - throw $exception; - } - } - - return $redirect; - } - - /** - * Handle request to delete a database host. - * - * @param int $host - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - */ - public function delete(int $host): RedirectResponse - { - $this->deletionService->handle($host); - $this->alert->success('The requested database host has been deleted from the system.')->flash(); - - return redirect()->route('admin.databases'); - } -} diff --git a/app/Http/Controllers/Admin/LocationController.php b/app/Http/Controllers/Admin/LocationController.php deleted file mode 100644 index 9239889cc..000000000 --- a/app/Http/Controllers/Admin/LocationController.php +++ /dev/null @@ -1,157 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin; - -use Pterodactyl\Models\Location; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Http\Requests\Admin\LocationFormRequest; -use Pterodactyl\Services\Locations\LocationUpdateService; -use Pterodactyl\Services\Locations\LocationCreationService; -use Pterodactyl\Services\Locations\LocationDeletionService; -use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; - -class LocationController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Services\Locations\LocationCreationService - */ - protected $creationService; - - /** - * @var \Pterodactyl\Services\Locations\LocationDeletionService - */ - protected $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Locations\LocationUpdateService - */ - protected $updateService; - - /** - * LocationController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Services\Locations\LocationCreationService $creationService - * @param \Pterodactyl\Services\Locations\LocationDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository - * @param \Pterodactyl\Services\Locations\LocationUpdateService $updateService - */ - public function __construct( - AlertsMessageBag $alert, - LocationCreationService $creationService, - LocationDeletionService $deletionService, - LocationRepositoryInterface $repository, - LocationUpdateService $updateService - ) { - $this->alert = $alert; - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->repository = $repository; - $this->updateService = $updateService; - } - - /** - * Return the location overview page. - * - * @return \Illuminate\View\View - */ - public function index() - { - return view('admin.locations.index', [ - 'locations' => $this->repository->getAllWithDetails(), - ]); - } - - /** - * Return the location view page. - * - * @param int $id - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function view($id) - { - return view('admin.locations.view', [ - 'location' => $this->repository->getWithNodes($id), - ]); - } - - /** - * Handle request to create new location. - * - * @param \Pterodactyl\Http\Requests\Admin\LocationFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Throwable - */ - public function create(LocationFormRequest $request) - { - $location = $this->creationService->handle($request->normalize()); - $this->alert->success('Location was created successfully.')->flash(); - - return redirect()->route('admin.locations.view', $location->id); - } - - /** - * Handle request to update or delete location. - * - * @param \Pterodactyl\Http\Requests\Admin\LocationFormRequest $request - * @param \Pterodactyl\Models\Location $location - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Throwable - */ - public function update(LocationFormRequest $request, Location $location) - { - if ($request->input('action') === 'delete') { - return $this->delete($location); - } - - $this->updateService->handle($location->id, $request->normalize()); - $this->alert->success('Location was updated successfully.')->flash(); - - return redirect()->route('admin.locations.view', $location->id); - } - - /** - * Delete a location from the system. - * - * @param \Pterodactyl\Models\Location $location - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\DisplayException - */ - public function delete(Location $location) - { - try { - $this->deletionService->handle($location->id); - - return redirect()->route('admin.locations'); - } catch (DisplayException $ex) { - $this->alert->danger($ex->getMessage())->flash(); - } - - return redirect()->route('admin.locations.view', $location->id); - } -} diff --git a/app/Http/Controllers/Admin/Nests/EggController.php b/app/Http/Controllers/Admin/Nests/EggController.php deleted file mode 100644 index 56d69e3a7..000000000 --- a/app/Http/Controllers/Admin/Nests/EggController.php +++ /dev/null @@ -1,128 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin\Nests; - -use Javascript; -use Illuminate\View\View; -use Pterodactyl\Models\Egg; -use Illuminate\Http\RedirectResponse; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Eggs\EggUpdateService; -use Pterodactyl\Services\Eggs\EggCreationService; -use Pterodactyl\Services\Eggs\EggDeletionService; -use Pterodactyl\Http\Requests\Admin\Egg\EggFormRequest; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Contracts\Repository\NestRepositoryInterface; - -class EggController extends Controller -{ - protected $alert; - protected $creationService; - protected $deletionService; - protected $nestRepository; - protected $repository; - protected $updateService; - - public function __construct( - AlertsMessageBag $alert, - EggCreationService $creationService, - EggDeletionService $deletionService, - EggRepositoryInterface $repository, - EggUpdateService $updateService, - NestRepositoryInterface $nestRepository - ) { - $this->alert = $alert; - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->nestRepository = $nestRepository; - $this->repository = $repository; - $this->updateService = $updateService; - } - - /** - * Handle a request to display the Egg creation page. - * - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function create(): View - { - $nests = $this->nestRepository->getWithEggs(); - Javascript::put(['nests' => $nests->keyBy('id')]); - - return view('admin.eggs.new', ['nests' => $nests]); - } - - /** - * Handle request to store a new Egg. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException - */ - public function store(EggFormRequest $request): RedirectResponse - { - $egg = $this->creationService->handle($request->normalize()); - $this->alert->success(trans('admin/nests.eggs.notices.egg_created'))->flash(); - - return redirect()->route('admin.nests.egg.view', $egg->id); - } - - /** - * Handle request to view a single Egg. - * - * @param \Pterodactyl\Models\Egg $egg - * @return \Illuminate\View\View - */ - public function view(Egg $egg): View - { - return view('admin.eggs.view', ['egg' => $egg]); - } - - /** - * Handle request to update an Egg. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggFormRequest $request - * @param \Pterodactyl\Models\Egg $egg - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException - */ - public function update(EggFormRequest $request, Egg $egg): RedirectResponse - { - $this->updateService->handle($egg, $request->normalize()); - $this->alert->success(trans('admin/nests.eggs.notices.updated'))->flash(); - - return redirect()->route('admin.nests.egg.view', $egg->id); - } - - /** - * Handle request to destroy an egg. - * - * @param \Pterodactyl\Models\Egg $egg - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Service\Egg\HasChildrenException - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - */ - public function destroy(Egg $egg): RedirectResponse - { - $this->deletionService->handle($egg->id); - $this->alert->success(trans('admin/nests.eggs.notices.deleted'))->flash(); - - return redirect()->route('admin.nests.view', $egg->nest_id); - } -} diff --git a/app/Http/Controllers/Admin/Nests/EggScriptController.php b/app/Http/Controllers/Admin/Nests/EggScriptController.php deleted file mode 100644 index ac67a2a6d..000000000 --- a/app/Http/Controllers/Admin/Nests/EggScriptController.php +++ /dev/null @@ -1,98 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin\Nests; - -use Illuminate\View\View; -use Illuminate\Http\RedirectResponse; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Eggs\Scripts\InstallScriptService; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Http\Requests\Admin\Egg\EggScriptFormRequest; - -class EggScriptController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Services\Eggs\Scripts\InstallScriptService - */ - protected $installScriptService; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - - /** - * EggScriptController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository - * @param \Pterodactyl\Services\Eggs\Scripts\InstallScriptService $installScriptService - */ - public function __construct( - AlertsMessageBag $alert, - EggRepositoryInterface $repository, - InstallScriptService $installScriptService - ) { - $this->alert = $alert; - $this->installScriptService = $installScriptService; - $this->repository = $repository; - } - - /** - * Handle requests to render installation script for an Egg. - * - * @param int $egg - * @return \Illuminate\View\View - */ - public function index(int $egg): View - { - $egg = $this->repository->getWithCopyAttributes($egg); - $copy = $this->repository->findWhere([ - ['copy_script_from', '=', null], - ['nest_id', '=', $egg->nest_id], - ['id', '!=', $egg], - ]); - - $rely = $this->repository->findWhere([ - ['copy_script_from', '=', $egg->id], - ]); - - return view('admin.eggs.scripts', [ - 'copyFromOptions' => $copy, - 'relyOnScript' => $rely, - 'egg' => $egg, - ]); - } - - /** - * Handle a request to update the installation script for an Egg. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggScriptFormRequest $request - * @param int $egg - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException - */ - public function update(EggScriptFormRequest $request, int $egg): RedirectResponse - { - $this->installScriptService->handle($egg, $request->normalize()); - $this->alert->success(trans('admin/nests.eggs.notices.script_updated'))->flash(); - - return redirect()->route('admin.nests.egg.scripts', $egg); - } -} diff --git a/app/Http/Controllers/Admin/Nests/EggShareController.php b/app/Http/Controllers/Admin/Nests/EggShareController.php deleted file mode 100644 index 80e8e30b9..000000000 --- a/app/Http/Controllers/Admin/Nests/EggShareController.php +++ /dev/null @@ -1,120 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin\Nests; - -use Pterodactyl\Models\Egg; -use Illuminate\Http\RedirectResponse; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Symfony\Component\HttpFoundation\Response; -use Pterodactyl\Services\Eggs\Sharing\EggExporterService; -use Pterodactyl\Services\Eggs\Sharing\EggImporterService; -use Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest; -use Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService; - -class EggShareController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Services\Eggs\Sharing\EggExporterService - */ - protected $exporterService; - - /** - * @var \Pterodactyl\Services\Eggs\Sharing\EggImporterService - */ - protected $importerService; - - /** - * @var \Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService - */ - protected $updateImporterService; - - /** - * OptionShareController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Services\Eggs\Sharing\EggExporterService $exporterService - * @param \Pterodactyl\Services\Eggs\Sharing\EggImporterService $importerService - * @param \Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService $updateImporterService - */ - public function __construct( - AlertsMessageBag $alert, - EggExporterService $exporterService, - EggImporterService $importerService, - EggUpdateImporterService $updateImporterService - ) { - $this->alert = $alert; - $this->exporterService = $exporterService; - $this->importerService = $importerService; - $this->updateImporterService = $updateImporterService; - } - - /** - * @param \Pterodactyl\Models\Egg $egg - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function export(Egg $egg): Response - { - $filename = trim(preg_replace('/[^\w]/', '-', kebab_case($egg->name)), '-'); - - return response($this->exporterService->handle($egg->id), 200, [ - 'Content-Transfer-Encoding' => 'binary', - 'Content-Description' => 'File Transfer', - 'Content-Disposition' => 'attachment; filename=egg-' . $filename . '.json', - 'Content-Type' => 'application/json', - ]); - } - - /** - * Import a new service option using an XML file. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException - */ - public function import(EggImportFormRequest $request): RedirectResponse - { - $egg = $this->importerService->handle($request->file('import_file'), $request->input('import_to_nest')); - $this->alert->success(trans('admin/nests.eggs.notices.imported'))->flash(); - - return redirect()->route('admin.nests.egg.view', ['egg' => $egg->id]); - } - - /** - * Update an existing Egg using a new imported file. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest $request - * @param int $egg - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException - */ - public function update(EggImportFormRequest $request, int $egg): RedirectResponse - { - $this->updateImporterService->handle($egg, $request->file('import_file')); - $this->alert->success(trans('admin/nests.eggs.notices.updated_via_import'))->flash(); - - return redirect()->route('admin.nests.egg.view', ['egg' => $egg]); - } -} diff --git a/app/Http/Controllers/Admin/Nests/EggVariableController.php b/app/Http/Controllers/Admin/Nests/EggVariableController.php deleted file mode 100644 index df29de5ae..000000000 --- a/app/Http/Controllers/Admin/Nests/EggVariableController.php +++ /dev/null @@ -1,147 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin\Nests; - -use Illuminate\View\View; -use Pterodactyl\Models\Egg; -use Pterodactyl\Models\EggVariable; -use Illuminate\Http\RedirectResponse; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Services\Eggs\Variables\VariableUpdateService; -use Pterodactyl\Http\Requests\Admin\Egg\EggVariableFormRequest; -use Pterodactyl\Services\Eggs\Variables\VariableCreationService; -use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; - -class EggVariableController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Services\Eggs\Variables\VariableCreationService - */ - protected $creationService; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Eggs\Variables\VariableUpdateService - */ - protected $updateService; - - /** - * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface - */ - protected $variableRepository; - - /** - * EggVariableController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Services\Eggs\Variables\VariableCreationService $creationService - * @param \Pterodactyl\Services\Eggs\Variables\VariableUpdateService $updateService - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $variableRepository - */ - public function __construct( - AlertsMessageBag $alert, - VariableCreationService $creationService, - VariableUpdateService $updateService, - EggRepositoryInterface $repository, - EggVariableRepositoryInterface $variableRepository - ) { - $this->alert = $alert; - $this->creationService = $creationService; - $this->repository = $repository; - $this->updateService = $updateService; - $this->variableRepository = $variableRepository; - } - - /** - * Handle request to view the variables attached to an Egg. - * - * @param int $egg - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function view(int $egg): View - { - $egg = $this->repository->getWithVariables($egg); - - return view('admin.eggs.variables', ['egg' => $egg]); - } - - /** - * Handle a request to create a new Egg variable. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggVariableFormRequest $request - * @param \Pterodactyl\Models\Egg $egg - * - * @return \Illuminate\Http\RedirectResponse - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException - * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException - */ - public function store(EggVariableFormRequest $request, Egg $egg): RedirectResponse - { - $this->creationService->handle($egg->id, $request->normalize()); - $this->alert->success(trans('admin/nests.variables.notices.variable_created'))->flash(); - - return redirect()->route('admin.nests.egg.variables', $egg->id); - } - - /** - * Handle a request to update an existing Egg variable. - * - * @param \Pterodactyl\Http\Requests\Admin\Egg\EggVariableFormRequest $request - * @param \Pterodactyl\Models\Egg $egg - * @param \Pterodactyl\Models\EggVariable $variable - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException - */ - public function update(EggVariableFormRequest $request, Egg $egg, EggVariable $variable): RedirectResponse - { - $this->updateService->handle($variable, $request->normalize()); - $this->alert->success(trans('admin/nests.variables.notices.variable_updated', [ - 'variable' => $variable->name, - ]))->flash(); - - return redirect()->route('admin.nests.egg.variables', $egg->id); - } - - /** - * Handle a request to delete an existing Egg variable from the Panel. - * - * @param int $egg - * @param \Pterodactyl\Models\EggVariable $variable - * @return \Illuminate\Http\RedirectResponse - */ - public function destroy(int $egg, EggVariable $variable): RedirectResponse - { - $this->variableRepository->delete($variable->id); - $this->alert->success(trans('admin/nests.variables.notices.variable_deleted', [ - 'variable' => $variable->name, - ]))->flash(); - - return redirect()->route('admin.nests.egg.variables', $egg); - } -} diff --git a/app/Http/Controllers/Admin/Nests/NestController.php b/app/Http/Controllers/Admin/Nests/NestController.php deleted file mode 100644 index b62753cad..000000000 --- a/app/Http/Controllers/Admin/Nests/NestController.php +++ /dev/null @@ -1,160 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin\Nests; - -use Illuminate\View\View; -use Illuminate\Http\RedirectResponse; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Nests\NestUpdateService; -use Pterodactyl\Services\Nests\NestCreationService; -use Pterodactyl\Services\Nests\NestDeletionService; -use Pterodactyl\Contracts\Repository\NestRepositoryInterface; -use Pterodactyl\Http\Requests\Admin\Nest\StoreNestFormRequest; - -class NestController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Services\Nests\NestCreationService - */ - protected $nestCreationService; - - /** - * @var \Pterodactyl\Services\Nests\NestDeletionService - */ - protected $nestDeletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Nests\NestUpdateService - */ - protected $nestUpdateService; - - /** - * NestController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Services\Nests\NestCreationService $nestCreationService - * @param \Pterodactyl\Services\Nests\NestDeletionService $nestDeletionService - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $repository - * @param \Pterodactyl\Services\Nests\NestUpdateService $nestUpdateService - */ - public function __construct( - AlertsMessageBag $alert, - NestCreationService $nestCreationService, - NestDeletionService $nestDeletionService, - NestRepositoryInterface $repository, - NestUpdateService $nestUpdateService - ) { - $this->alert = $alert; - $this->nestDeletionService = $nestDeletionService; - $this->nestCreationService = $nestCreationService; - $this->nestUpdateService = $nestUpdateService; - $this->repository = $repository; - } - - /** - * Render nest listing page. - * - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function index(): View - { - return view('admin.nests.index', [ - 'nests' => $this->repository->getWithCounts(), - ]); - } - - /** - * Render nest creation page. - * - * @return \Illuminate\View\View - */ - public function create(): View - { - return view('admin.nests.new'); - } - - /** - * Handle the storage of a new nest. - * - * @param \Pterodactyl\Http\Requests\Admin\Nest\StoreNestFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function store(StoreNestFormRequest $request): RedirectResponse - { - $nest = $this->nestCreationService->handle($request->normalize()); - $this->alert->success(trans('admin/nests.notices.created', ['name' => $nest->name]))->flash(); - - return redirect()->route('admin.nests.view', $nest->id); - } - - /** - * Return details about a nest including all of the eggs and servers per egg. - * - * @param int $nest - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function view(int $nest): View - { - return view('admin.nests.view', [ - 'nest' => $this->repository->getWithEggServers($nest), - ]); - } - - /** - * Handle request to update a nest. - * - * @param \Pterodactyl\Http\Requests\Admin\Nest\StoreNestFormRequest $request - * @param int $nest - * - * @return \Illuminate\Http\RedirectResponse - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(StoreNestFormRequest $request, int $nest): RedirectResponse - { - $this->nestUpdateService->handle($nest, $request->normalize()); - $this->alert->success(trans('admin/nests.notices.updated'))->flash(); - - return redirect()->route('admin.nests.view', $nest); - } - - /** - * Handle request to delete a nest. - * - * @param int $nest - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - */ - public function destroy(int $nest): RedirectResponse - { - $this->nestDeletionService->handle($nest); - $this->alert->success(trans('admin/nests.notices.deleted'))->flash(); - - return redirect()->route('admin.nests'); - } -} diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php deleted file mode 100644 index 28266eac5..000000000 --- a/app/Http/Controllers/Admin/NodesController.php +++ /dev/null @@ -1,403 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin; - -use Javascript; -use Illuminate\Http\Request; -use Pterodactyl\Models\Node; -use Illuminate\Http\Response; -use Pterodactyl\Models\Allocation; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Nodes\NodeUpdateService; -use Illuminate\Cache\Repository as CacheRepository; -use Pterodactyl\Services\Nodes\NodeCreationService; -use Pterodactyl\Services\Nodes\NodeDeletionService; -use Pterodactyl\Services\Allocations\AssignmentService; -use Pterodactyl\Services\Helpers\SoftwareVersionService; -use Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Http\Requests\Admin\Node\AllocationFormRequest; -use Pterodactyl\Services\Allocations\AllocationDeletionService; -use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; -use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; -use Pterodactyl\Http\Requests\Admin\Node\AllocationAliasFormRequest; - -class NodesController extends Controller -{ - /** - * @var \Pterodactyl\Services\Allocations\AllocationDeletionService - */ - protected $allocationDeletionService; - - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - protected $allocationRepository; - - /** - * @var \Pterodactyl\Services\Allocations\AssignmentService - */ - protected $assignmentService; - - /** - * @var \Illuminate\Cache\Repository - */ - protected $cache; - - /** - * @var \Pterodactyl\Services\Nodes\NodeCreationService - */ - protected $creationService; - - /** - * @var \Pterodactyl\Services\Nodes\NodeDeletionService - */ - protected $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $locationRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * @var \Pterodactyl\Services\Nodes\NodeUpdateService - */ - protected $updateService; - - /** - * @var \Pterodactyl\Services\Helpers\SoftwareVersionService - */ - protected $versionService; - - /** - * NodesController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Services\Allocations\AllocationDeletionService $allocationDeletionService - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository - * @param \Pterodactyl\Services\Allocations\AssignmentService $assignmentService - * @param \Illuminate\Cache\Repository $cache - * @param \Pterodactyl\Services\Nodes\NodeCreationService $creationService - * @param \Pterodactyl\Services\Nodes\NodeDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $locationRepository - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Services\Nodes\NodeUpdateService $updateService - * @param \Pterodactyl\Services\Helpers\SoftwareVersionService $versionService - */ - public function __construct( - AlertsMessageBag $alert, - AllocationDeletionService $allocationDeletionService, - AllocationRepositoryInterface $allocationRepository, - AssignmentService $assignmentService, - CacheRepository $cache, - NodeCreationService $creationService, - NodeDeletionService $deletionService, - LocationRepositoryInterface $locationRepository, - NodeRepositoryInterface $repository, - ServerRepositoryInterface $serverRepository, - NodeUpdateService $updateService, - SoftwareVersionService $versionService - ) { - $this->alert = $alert; - $this->allocationDeletionService = $allocationDeletionService; - $this->allocationRepository = $allocationRepository; - $this->assignmentService = $assignmentService; - $this->cache = $cache; - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->locationRepository = $locationRepository; - $this->repository = $repository; - $this->serverRepository = $serverRepository; - $this->updateService = $updateService; - $this->versionService = $versionService; - } - - /** - * Displays the index page listing all nodes on the panel. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request) - { - return view('admin.nodes.index', [ - 'nodes' => $this->repository->setSearchTerm($request->input('query'))->getNodeListingData(), - ]); - } - - /** - * Displays create new node page. - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View - */ - public function create() - { - $locations = $this->locationRepository->all(); - if (count($locations) < 1) { - $this->alert->warning(trans('admin/node.notices.location_required'))->flash(); - - return redirect()->route('admin.locations'); - } - - return view('admin.nodes.new', ['locations' => $locations]); - } - - /** - * Post controller to create a new node on the system. - * - * @param \Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function store(NodeFormRequest $request) - { - $node = $this->creationService->handle($request->normalize()); - $this->alert->info(trans('admin/node.notices.node_created'))->flash(); - - return redirect()->route('admin.nodes.view.allocation', $node->id); - } - - /** - * Shows the index overview page for a specific node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\View\View - */ - public function viewIndex(Node $node) - { - return view('admin.nodes.view.index', [ - 'node' => $this->repository->loadLocationAndServerCount($node), - 'stats' => $this->repository->getUsageStats($node), - 'version' => $this->versionService, - ]); - } - - /** - * Shows the settings page for a specific node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\View\View - */ - public function viewSettings(Node $node) - { - return view('admin.nodes.view.settings', [ - 'node' => $node, - 'locations' => $this->locationRepository->all(), - ]); - } - - /** - * Shows the configuration page for a specific node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\View\View - */ - public function viewConfiguration(Node $node) - { - return view('admin.nodes.view.configuration', ['node' => $node]); - } - - /** - * Shows the allocation page for a specific node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\View\View - */ - public function viewAllocation(Node $node) - { - $this->repository->loadNodeAllocations($node); - Javascript::put(['node' => collect($node)->only(['id'])]); - - return view('admin.nodes.view.allocation', [ - 'allocations' => $this->allocationRepository->setColumns(['ip'])->getUniqueAllocationIpsForNode($node->id), - 'node' => $node, - ]); - } - - /** - * Shows the server listing page for a specific node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\View\View - */ - public function viewServers(Node $node) - { - $servers = $this->serverRepository->loadAllServersForNode($node->id, 25); - Javascript::put([ - 'node' => collect($node->makeVisible('daemonSecret'))->only(['scheme', 'fqdn', 'daemonListen', 'daemonSecret']), - ]); - - return view('admin.nodes.view.servers', ['node' => $node, 'servers' => $servers]); - } - - /** - * Updates settings for a node. - * - * @param \Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest $request - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function updateSettings(NodeFormRequest $request, Node $node) - { - $this->updateService->handle($node, $request->normalize(), $request->input('reset_secret') === 'on'); - $this->alert->success(trans('admin/node.notices.node_updated'))->flash(); - - return redirect()->route('admin.nodes.view.settings', $node->id)->withInput(); - } - - /** - * Removes a single allocation from a node. - * - * @param int $node - * @param \Pterodactyl\Models\Allocation $allocation - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException - */ - public function allocationRemoveSingle(int $node, Allocation $allocation): Response - { - $this->allocationDeletionService->handle($allocation); - - return response('', 204); - } - - /** - * Removes multiple individual allocations from a node. - * - * @param \Illuminate\Http\Request $request - * @param int $node - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException - */ - public function allocationRemoveMultiple(Request $request, int $node): Response - { - $allocations = $request->input('allocations'); - foreach ($allocations as $rawAllocation) { - $allocation = new Allocation(); - $allocation->id = $rawAllocation['id']; - $this->allocationRemoveSingle($node, $allocation); - } - - return response('', 204); - } - - /** - * Remove all allocations for a specific IP at once on a node. - * - * @param \Illuminate\Http\Request $request - * @param int $node - * @return \Illuminate\Http\RedirectResponse - */ - public function allocationRemoveBlock(Request $request, $node) - { - $this->allocationRepository->deleteWhere([ - ['node_id', '=', $node], - ['server_id', '=', null], - ['ip', '=', $request->input('ip')], - ]); - - $this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => $request->input('ip')])) - ->flash(); - - return redirect()->route('admin.nodes.view.allocation', $node); - } - - /** - * Sets an alias for a specific allocation on a node. - * - * @param \Pterodactyl\Http\Requests\Admin\Node\AllocationAliasFormRequest $request - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function allocationSetAlias(AllocationAliasFormRequest $request) - { - $this->allocationRepository->update($request->input('allocation_id'), [ - 'ip_alias' => (empty($request->input('alias'))) ? null : $request->input('alias'), - ]); - - return response('', 204); - } - - /** - * Creates new allocations on a node. - * - * @param \Pterodactyl\Http\Requests\Admin\Node\AllocationFormRequest $request - * @param int|\Pterodactyl\Models\Node $node - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException - * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException - * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException - * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException - */ - public function createAllocation(AllocationFormRequest $request, Node $node) - { - $this->assignmentService->handle($node, $request->normalize()); - $this->alert->success(trans('admin/node.notices.allocations_added'))->flash(); - - return redirect()->route('admin.nodes.view.allocation', $node->id); - } - - /** - * Deletes a node from the system. - * - * @param $node - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - */ - public function delete($node) - { - $this->deletionService->handle($node); - $this->alert->success(trans('admin/node.notices.node_deleted'))->flash(); - - return redirect()->route('admin.nodes'); - } - - /** - * Returns the configuration token to auto-deploy a node. - * - * @param \Pterodactyl\Models\Node $node - * @return \Illuminate\Http\JsonResponse - */ - public function setToken(Node $node) - { - $token = bin2hex(random_bytes(16)); - $this->cache->put('Node:Configuration:' . $token, $node->id, 5); - - return response()->json(['token' => $token]); - } -} diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php deleted file mode 100644 index 1dd7c881c..000000000 --- a/app/Http/Controllers/Admin/PackController.php +++ /dev/null @@ -1,250 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin; - -use Illuminate\Http\Request; -use Pterodactyl\Models\Pack; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Packs\ExportPackService; -use Pterodactyl\Services\Packs\PackUpdateService; -use Pterodactyl\Services\Packs\PackCreationService; -use Pterodactyl\Services\Packs\PackDeletionService; -use Pterodactyl\Http\Requests\Admin\PackFormRequest; -use Pterodactyl\Services\Packs\TemplateUploadService; -use Pterodactyl\Contracts\Repository\NestRepositoryInterface; -use Pterodactyl\Contracts\Repository\PackRepositoryInterface; -use Illuminate\Contracts\Config\Repository as ConfigRepository; - -class PackController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Pterodactyl\Services\Packs\PackCreationService - */ - protected $creationService; - - /** - * @var \Pterodactyl\Services\Packs\PackDeletionService - */ - protected $deletionService; - - /** - * @var \Pterodactyl\Services\Packs\ExportPackService - */ - protected $exportService; - - /** - * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Packs\PackUpdateService - */ - protected $updateService; - - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - protected $serviceRepository; - - /** - * @var \Pterodactyl\Services\Packs\TemplateUploadService - */ - protected $templateUploadService; - - /** - * PackController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Services\Packs\ExportPackService $exportService - * @param \Pterodactyl\Services\Packs\PackCreationService $creationService - * @param \Pterodactyl\Services\Packs\PackDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\PackRepositoryInterface $repository - * @param \Pterodactyl\Services\Packs\PackUpdateService $updateService - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $serviceRepository - * @param \Pterodactyl\Services\Packs\TemplateUploadService $templateUploadService - */ - public function __construct( - AlertsMessageBag $alert, - ConfigRepository $config, - ExportPackService $exportService, - PackCreationService $creationService, - PackDeletionService $deletionService, - PackRepositoryInterface $repository, - PackUpdateService $updateService, - NestRepositoryInterface $serviceRepository, - TemplateUploadService $templateUploadService - ) { - $this->alert = $alert; - $this->config = $config; - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->exportService = $exportService; - $this->repository = $repository; - $this->updateService = $updateService; - $this->serviceRepository = $serviceRepository; - $this->templateUploadService = $templateUploadService; - } - - /** - * Display listing of all packs on the system. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request) - { - return view('admin.packs.index', [ - 'packs' => $this->repository->setSearchTerm($request->input('query'))->paginateWithEggAndServerCount(), - ]); - } - - /** - * Display new pack creation form. - * - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function create() - { - return view('admin.packs.new', [ - 'nests' => $this->serviceRepository->getWithEggs(), - ]); - } - - /** - * Display new pack creation modal for use with template upload. - * - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function newTemplate() - { - return view('admin.packs.modal', [ - 'nests' => $this->serviceRepository->getWithEggs(), - ]); - } - - /** - * Handle create pack request and route user to location. - * - * @param \Pterodactyl\Http\Requests\Admin\PackFormRequest $request - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException - * @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException - * @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException - */ - public function store(PackFormRequest $request) - { - if ($request->filled('from_template')) { - $pack = $this->templateUploadService->handle($request->input('egg_id'), $request->file('file_upload')); - } else { - $pack = $this->creationService->handle($request->normalize(), $request->file('file_upload')); - } - - $this->alert->success(trans('admin/pack.notices.pack_created'))->flash(); - - return redirect()->route('admin.packs.view', $pack->id); - } - - /** - * Display pack view template to user. - * - * @param \Pterodactyl\Models\Pack $pack - * @return \Illuminate\View\View - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function view(Pack $pack) - { - return view('admin.packs.view', [ - 'pack' => $this->repository->loadServerData($pack), - 'nests' => $this->serviceRepository->getWithEggs(), - ]); - } - - /** - * Handle updating or deleting pack information. - * - * @param \Pterodactyl\Http\Requests\Admin\PackFormRequest $request - * @param \Pterodactyl\Models\Pack $pack - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - */ - public function update(PackFormRequest $request, Pack $pack) - { - $this->updateService->handle($pack, $request->normalize()); - $this->alert->success(trans('admin/pack.notices.pack_updated'))->flash(); - - return redirect()->route('admin.packs.view', $pack->id); - } - - /** - * Delete a pack if no servers are attached to it currently. - * - * @param \Pterodactyl\Models\Pack $pack - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - */ - public function destroy(Pack $pack) - { - $this->deletionService->handle($pack->id); - $this->alert->success(trans('admin/pack.notices.pack_deleted', [ - 'name' => $pack->name, - ]))->flash(); - - return redirect()->route('admin.packs'); - } - - /** - * Creates an archive of the pack and downloads it to the browser. - * - * @param \Pterodactyl\Models\Pack $pack - * @param bool|string $files - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Pack\ZipArchiveCreationException - */ - public function export(Pack $pack, $files = false) - { - $filename = $this->exportService->handle($pack, is_string($files)); - - if (is_string($files)) { - return response()->download($filename, 'pack-' . $pack->name . '.zip')->deleteFileAfterSend(true); - } - - return response()->download($filename, 'pack-' . $pack->name . '.json', [ - 'Content-Type' => 'application/json', - ])->deleteFileAfterSend(true); - } -} diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php deleted file mode 100644 index 6bf21b249..000000000 --- a/app/Http/Controllers/Admin/ServersController.php +++ /dev/null @@ -1,627 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Admin; - -use Javascript; -use Illuminate\Http\Request; -use Pterodactyl\Models\User; -use Pterodactyl\Models\Server; -use Prologue\Alerts\AlertsMessageBag; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Servers\SuspensionService; -use Pterodactyl\Http\Requests\Admin\ServerFormRequest; -use Pterodactyl\Services\Servers\ServerCreationService; -use Pterodactyl\Services\Servers\ServerDeletionService; -use Pterodactyl\Services\Servers\ReinstallServerService; -use Pterodactyl\Services\Servers\ContainerRebuildService; -use Pterodactyl\Services\Servers\BuildModificationService; -use Pterodactyl\Services\Databases\DatabasePasswordService; -use Pterodactyl\Services\Servers\DetailsModificationService; -use Pterodactyl\Services\Servers\StartupModificationService; -use Pterodactyl\Contracts\Repository\NestRepositoryInterface; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; -use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository; -use Pterodactyl\Services\Databases\DatabaseManagementService; -use Illuminate\Contracts\Config\Repository as ConfigRepository; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; -use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; -use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; -use Pterodactyl\Http\Requests\Admin\Servers\Databases\StoreServerDatabaseRequest; - -class ServersController extends Controller -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - protected $alert; - - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - protected $allocationRepository; - - /** - * @var \Pterodactyl\Services\Servers\BuildModificationService - */ - protected $buildModificationService; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Pterodactyl\Services\Servers\ContainerRebuildService - */ - protected $containerRebuildService; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - protected $databaseRepository; - - /** - * @var \Pterodactyl\Services\Databases\DatabaseManagementService - */ - protected $databaseManagementService; - - /** - * @var \Pterodactyl\Services\Databases\DatabasePasswordService - */ - protected $databasePasswordService; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface - */ - protected $databaseHostRepository; - - /** - * @var \Pterodactyl\Services\Servers\ServerDeletionService - */ - protected $deletionService; - - /** - * @var \Pterodactyl\Services\Servers\DetailsModificationService - */ - protected $detailsModificationService; - - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $locationRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - protected $nestRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - protected $nodeRepository; - - /** - * @var \Pterodactyl\Services\Servers\ReinstallServerService - */ - protected $reinstallService; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Servers\ServerCreationService - */ - protected $service; - - /** - * @var \Pterodactyl\Services\Servers\StartupModificationService - */ - private $startupModificationService; - - /** - * @var \Pterodactyl\Services\Servers\SuspensionService - */ - protected $suspensionService; - - /** - * ServersController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository - * @param \Pterodactyl\Services\Servers\BuildModificationService $buildModificationService - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Services\Servers\ContainerRebuildService $containerRebuildService - * @param \Pterodactyl\Services\Servers\ServerCreationService $service - * @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService - * @param \Pterodactyl\Services\Databases\DatabasePasswordService $databasePasswordService - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository - * @param \Pterodactyl\Repositories\Eloquent\DatabaseHostRepository $databaseHostRepository - * @param \Pterodactyl\Services\Servers\ServerDeletionService $deletionService - * @param \Pterodactyl\Services\Servers\DetailsModificationService $detailsModificationService - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $locationRepository - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $nodeRepository - * @param \Pterodactyl\Services\Servers\ReinstallServerService $reinstallService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $nestRepository - * @param \Pterodactyl\Services\Servers\StartupModificationService $startupModificationService - * @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService - */ - public function __construct( - AlertsMessageBag $alert, - AllocationRepositoryInterface $allocationRepository, - BuildModificationService $buildModificationService, - ConfigRepository $config, - ContainerRebuildService $containerRebuildService, - ServerCreationService $service, - DatabaseManagementService $databaseManagementService, - DatabasePasswordService $databasePasswordService, - DatabaseRepositoryInterface $databaseRepository, - DatabaseHostRepository $databaseHostRepository, - ServerDeletionService $deletionService, - DetailsModificationService $detailsModificationService, - LocationRepositoryInterface $locationRepository, - NodeRepositoryInterface $nodeRepository, - ReinstallServerService $reinstallService, - ServerRepositoryInterface $repository, - NestRepositoryInterface $nestRepository, - StartupModificationService $startupModificationService, - SuspensionService $suspensionService - ) { - $this->alert = $alert; - $this->allocationRepository = $allocationRepository; - $this->buildModificationService = $buildModificationService; - $this->config = $config; - $this->containerRebuildService = $containerRebuildService; - $this->databaseHostRepository = $databaseHostRepository; - $this->databaseManagementService = $databaseManagementService; - $this->databasePasswordService = $databasePasswordService; - $this->databaseRepository = $databaseRepository; - $this->detailsModificationService = $detailsModificationService; - $this->deletionService = $deletionService; - $this->locationRepository = $locationRepository; - $this->nestRepository = $nestRepository; - $this->nodeRepository = $nodeRepository; - $this->reinstallService = $reinstallService; - $this->repository = $repository; - $this->service = $service; - $this->startupModificationService = $startupModificationService; - $this->suspensionService = $suspensionService; - } - - /** - * Display the index page with all servers currently on the system. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request) - { - return view('admin.servers.index', [ - 'servers' => $this->repository->setSearchTerm($request->input('query'))->getAllServers( - $this->config->get('pterodactyl.paginate.admin.servers') - ), - ]); - } - - /** - * Display create new server page. - * - * @return \Illuminate\View\View - * - * @throws \Exception - */ - public function create() - { - $nodes = $this->nodeRepository->all(); - if (count($nodes) < 1) { - $this->alert->warning(trans('admin/server.alerts.node_required'))->flash(); - - return redirect()->route('admin.nodes'); - } - - $nests = $this->nestRepository->getWithEggs(); - - Javascript::put([ - 'nodeData' => $this->nodeRepository->getNodesForServerCreation(), - 'nests' => $nests->map(function ($item) { - return array_merge($item->toArray(), [ - 'eggs' => $item->eggs->keyBy('id')->toArray(), - ]); - })->keyBy('id'), - ]); - - return view('admin.servers.new', [ - 'locations' => $this->locationRepository->all(), - 'nests' => $nests, - ]); - } - - /** - * Handle POST of server creation form. - * - * @param \Pterodactyl\Http\Requests\Admin\ServerFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException - * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException - */ - public function store(ServerFormRequest $request) - { - $server = $this->service->handle($request->except('_token')); - $this->alert->success(trans('admin/server.alerts.server_created'))->flash(); - - return redirect()->route('admin.servers.view', $server->id); - } - - /** - * Display the index when viewing a specific server. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\View\View - */ - public function viewIndex(Server $server) - { - return view('admin.servers.view.index', ['server' => $server]); - } - - /** - * Display the details page when viewing a specific server. - * - * @param int $server - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function viewDetails($server) - { - return view('admin.servers.view.details', [ - 'server' => $this->repository->findFirstWhere([ - ['id', '=', $server], - ['installed', '=', 1], - ]), - ]); - } - - /** - * Display the build details page when viewing a specific server. - * - * @param int $server - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function viewBuild($server) - { - $server = $this->repository->findFirstWhere([ - ['id', '=', $server], - ['installed', '=', 1], - ]); - - $allocations = $this->allocationRepository->getAllocationsForNode($server->node_id); - - return view('admin.servers.view.build', [ - 'server' => $server, - 'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'), - 'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'), - ]); - } - - /** - * Display startup configuration page for a server. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function viewStartup(Server $server) - { - $parameters = $this->repository->getVariablesWithValues($server->id, true); - if (! $parameters->server->installed) { - abort(404); - } - - $nests = $this->nestRepository->getWithEggs(); - - Javascript::put([ - 'server' => $server, - 'nests' => $nests->map(function ($item) { - return array_merge($item->toArray(), [ - 'eggs' => $item->eggs->keyBy('id')->toArray(), - ]); - })->keyBy('id'), - 'server_variables' => $parameters->data, - ]); - - return view('admin.servers.view.startup', [ - 'server' => $parameters->server, - 'nests' => $nests, - ]); - } - - /** - * Display the database management page for a specific server. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\View\View - */ - public function viewDatabase(Server $server) - { - $this->repository->loadDatabaseRelations($server); - - return view('admin.servers.view.database', [ - 'hosts' => $this->databaseHostRepository->all(), - 'server' => $server, - ]); - } - - /** - * Display the management page when viewing a specific server. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\DisplayException - */ - public function viewManage(Server $server) - { - if ($server->installed > 1) { - throw new DisplayException('This server is in a failed installation state and must be deleted and recreated.'); - } - - return view('admin.servers.view.manage', ['server' => $server]); - } - - /** - * Display the deletion page for a server. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\View\View - */ - public function viewDelete(Server $server) - { - return view('admin.servers.view.delete', ['server' => $server]); - } - - /** - * Update the details for a server. - * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function setDetails(Request $request, Server $server) - { - $this->detailsModificationService->handle($server, $request->only([ - 'owner_id', 'external_id', 'name', 'description', - ])); - - $this->alert->success(trans('admin/server.alerts.details_updated'))->flash(); - - return redirect()->route('admin.servers.view.details', $server->id); - } - - /** - * Toggles the install status for a server. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function toggleInstall(Server $server) - { - if ($server->installed > 1) { - throw new DisplayException(trans('admin/server.exceptions.marked_as_failed')); - } - - $this->repository->update($server->id, [ - 'installed' => ! $server->installed, - ], true, true); - - $this->alert->success(trans('admin/server.alerts.install_toggled'))->flash(); - - return redirect()->route('admin.servers.view.manage', $server->id); - } - - /** - * Reinstalls the server with the currently assigned pack and service. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function reinstallServer(Server $server) - { - $this->reinstallService->reinstall($server); - $this->alert->success(trans('admin/server.alerts.server_reinstalled'))->flash(); - - return redirect()->route('admin.servers.view.manage', $server->id); - } - - /** - * Setup a server to have a container rebuild. - * - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - */ - public function rebuildContainer(Server $server) - { - $this->containerRebuildService->handle($server); - $this->alert->success(trans('admin/server.alerts.rebuild_on_boot'))->flash(); - - return redirect()->route('admin.servers.view.manage', $server->id); - } - - /** - * Manage the suspension status for a server. - * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function manageSuspension(Request $request, Server $server) - { - $this->suspensionService->toggle($server, $request->input('action')); - $this->alert->success(trans('admin/server.alerts.suspension_toggled', [ - 'status' => $request->input('action') . 'ed', - ]))->flash(); - - return redirect()->route('admin.servers.view.manage', $server->id); - } - - /** - * Update the build configuration for a server. - * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function updateBuild(Request $request, Server $server) - { - $this->buildModificationService->handle($server, $request->only([ - 'allocation_id', 'add_allocations', 'remove_allocations', - 'memory', 'swap', 'io', 'cpu', 'disk', - 'database_limit', 'allocation_limit', 'oom_disabled', - ])); - $this->alert->success(trans('admin/server.alerts.build_updated'))->flash(); - - return redirect()->route('admin.servers.view.build', $server->id); - } - - /** - * Start the server deletion process. - * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function delete(Request $request, Server $server) - { - $this->deletionService->withForce($request->filled('force_delete'))->handle($server); - $this->alert->success(trans('admin/server.alerts.server_deleted'))->flash(); - - return redirect()->route('admin.servers'); - } - - /** - * Update the startup command as well as variables. - * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function saveStartup(Request $request, Server $server) - { - $this->startupModificationService->setUserLevel(User::USER_LEVEL_ADMIN); - $this->startupModificationService->handle($server, $request->except('_token')); - $this->alert->success(trans('admin/server.alerts.startup_changed'))->flash(); - - return redirect()->route('admin.servers.view.startup', $server->id); - } - - /** - * Creates a new database assigned to a specific server. - * - * @param \Pterodactyl\Http\Requests\Admin\Servers\Databases\StoreServerDatabaseRequest $request - * @param int $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - */ - public function newDatabase(StoreServerDatabaseRequest $request, $server) - { - $this->databaseManagementService->create($server, [ - 'database' => $request->input('database'), - 'remote' => $request->input('remote'), - 'database_host_id' => $request->input('database_host_id'), - ]); - - return redirect()->route('admin.servers.view.database', $server)->withInput(); - } - - /** - * Resets the database password for a specific database on this server. - * - * @param \Illuminate\Http\Request $request - * @param int $server - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Throwable - */ - public function resetDatabasePassword(Request $request, $server) - { - $database = $this->databaseRepository->findFirstWhere([ - ['server_id', '=', $server], - ['id', '=', $request->input('database')], - ]); - - $this->databasePasswordService->handle($database); - - return response('', 204); - } - - /** - * Deletes a database from a server. - * - * @param int $server - * @param int $database - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function deleteDatabase($server, $database) - { - $database = $this->databaseRepository->findFirstWhere([ - ['server_id', '=', $server], - ['id', '=', $database], - ]); - - $this->databaseManagementService->delete($database->id); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Admin/Settings/AdvancedController.php b/app/Http/Controllers/Admin/Settings/AdvancedController.php deleted file mode 100644 index f32517e7a..000000000 --- a/app/Http/Controllers/Admin/Settings/AdvancedController.php +++ /dev/null @@ -1,93 +0,0 @@ -alert = $alert; - $this->config = $config; - $this->kernel = $kernel; - $this->settings = $settings; - } - - /** - * Render advanced Panel settings UI. - * - * @return \Illuminate\View\View - */ - public function index(): View - { - $showRecaptchaWarning = false; - if ( - $this->config->get('recaptcha._shipped_secret_key') === $this->config->get('recaptcha.secret_key') - || $this->config->get('recaptcha._shipped_website_key') === $this->config->get('recaptcha.website_key') - ) { - $showRecaptchaWarning = true; - } - - return view('admin.settings.advanced', [ - 'showRecaptchaWarning' => $showRecaptchaWarning, - ]); - } - - /** - * @param \Pterodactyl\Http\Requests\Admin\Settings\AdvancedSettingsFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(AdvancedSettingsFormRequest $request): RedirectResponse - { - foreach ($request->normalize() as $key => $value) { - $this->settings->set('settings::' . $key, $value); - } - - $this->kernel->call('queue:restart'); - $this->alert->success('Advanced settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash(); - - return redirect()->route('admin.settings.advanced'); - } -} diff --git a/app/Http/Controllers/Admin/Settings/IndexController.php b/app/Http/Controllers/Admin/Settings/IndexController.php deleted file mode 100644 index bb9aef3e4..000000000 --- a/app/Http/Controllers/Admin/Settings/IndexController.php +++ /dev/null @@ -1,91 +0,0 @@ -alert = $alert; - $this->kernel = $kernel; - $this->settings = $settings; - $this->versionService = $versionService; - } - - /** - * Render the UI for basic Panel settings. - * - * @return \Illuminate\View\View - */ - public function index(): View - { - return view('admin.settings.index', [ - 'version' => $this->versionService, - 'languages' => $this->getAvailableLanguages(true), - ]); - } - - /** - * Handle settings update. - * - * @param \Pterodactyl\Http\Requests\Admin\Settings\BaseSettingsFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(BaseSettingsFormRequest $request): RedirectResponse - { - foreach ($request->normalize() as $key => $value) { - $this->settings->set('settings::' . $key, $value); - } - - $this->kernel->call('queue:restart'); - $this->alert->success('Panel settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash(); - - return redirect()->route('admin.settings'); - } -} diff --git a/app/Http/Controllers/Admin/Settings/MailController.php b/app/Http/Controllers/Admin/Settings/MailController.php deleted file mode 100644 index 7364a4ec5..000000000 --- a/app/Http/Controllers/Admin/Settings/MailController.php +++ /dev/null @@ -1,135 +0,0 @@ -alert = $alert; - $this->config = $config; - $this->encrypter = $encrypter; - $this->kernel = $kernel; - $this->settings = $settings; - } - - /** - * Render UI for editing mail settings. This UI should only display if - * the server is configured to send mail using SMTP. - * - * @return \Illuminate\View\View - */ - public function index(): View - { - return view('admin.settings.mail', [ - 'disabled' => $this->config->get('mail.driver') !== 'smtp', - ]); - } - - /** - * Handle request to update SMTP mail settings. - * - * @param \Pterodactyl\Http\Requests\Admin\Settings\MailSettingsFormRequest $request - * @return \Illuminate\Http\Response - * - * @throws DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(MailSettingsFormRequest $request): Response - { - if ($this->config->get('mail.driver') !== 'smtp') { - throw new DisplayException('This feature is only available if SMTP is the selected email driver for the Panel.'); - } - - $values = $request->normalize(); - if (array_get($values, 'mail:password') === '!e') { - $values['mail:password'] = ''; - } - - foreach ($values as $key => $value) { - if (in_array($key, SettingsServiceProvider::getEncryptedKeys()) && ! empty($value)) { - $value = $this->encrypter->encrypt($value); - } - - $this->settings->set('settings::' . $key, $value); - } - - $this->kernel->call('queue:restart'); - - return response('', 204); - } - - /** - * Submit a request to send a test mail message. - * - * @param Request $request - * @return \Illuminate\Http\Response - */ - public function test(Request $request): Response - { - try { - Notification::route('mail', $request->user()->email) - ->notify(new MailTested($request->user())); - } catch (Exception $exception) { - return response($exception->getMessage(), 500); - } - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Admin/StatisticsController.php b/app/Http/Controllers/Admin/StatisticsController.php deleted file mode 100644 index 1ae807565..000000000 --- a/app/Http/Controllers/Admin/StatisticsController.php +++ /dev/null @@ -1,97 +0,0 @@ -allocationRepository = $allocationRepository; - $this->databaseRepository = $databaseRepository; - $this->eggRepository = $eggRepository; - $this->nodeRepository = $nodeRepository; - $this->serverRepository = $serverRepository; - $this->userRepository = $userRepository; - } - - public function index() - { - $servers = $this->serverRepository->all(); - $nodes = $this->nodeRepository->all(); - $usersCount = $this->userRepository->count(); - $eggsCount = $this->eggRepository->count(); - $databasesCount = $this->databaseRepository->count(); - $totalAllocations = $this->allocationRepository->count(); - $suspendedServersCount = $this->serverRepository->getSuspendedServersCount(); - - $totalServerRam = 0; - $totalNodeRam = 0; - $totalServerDisk = 0; - $totalNodeDisk = 0; - foreach ($nodes as $node) { - $stats = $this->nodeRepository->getUsageStatsRaw($node); - $totalServerRam += $stats['memory']['value']; - $totalNodeRam += $stats['memory']['max']; - $totalServerDisk += $stats['disk']['value']; - $totalNodeDisk += $stats['disk']['max']; - } - - $tokens = []; - foreach ($nodes as $node) { - $tokens[$node->id] = $node->daemonSecret; - } - - $this->injectJavascript([ - 'servers' => $servers, - 'suspendedServers' => $suspendedServersCount, - 'totalServerRam' => $totalServerRam, - 'totalNodeRam' => $totalNodeRam, - 'totalServerDisk' => $totalServerDisk, - 'totalNodeDisk' => $totalNodeDisk, - 'nodes' => $nodes, - 'tokens' => $tokens, - ]); - - return view('admin.statistics', [ - 'servers' => $servers, - 'nodes' => $nodes, - 'usersCount' => $usersCount, - 'eggsCount' => $eggsCount, - 'totalServerRam' => $totalServerRam, - 'databasesCount' => $databasesCount, - 'totalNodeRam' => $totalNodeRam, - 'totalNodeDisk' => $totalNodeDisk, - 'totalServerDisk' => $totalServerDisk, - 'totalAllocations' => $totalAllocations, - ]); - } -} diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php deleted file mode 100644 index e0474fa5b..000000000 --- a/app/Http/Controllers/Admin/UserController.php +++ /dev/null @@ -1,202 +0,0 @@ -alert = $alert; - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->repository = $repository; - $this->translator = $translator; - $this->updateService = $updateService; - } - - /** - * Display user index page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request) - { - $users = $this->repository->setSearchTerm($request->input('query'))->getAllUsersWithCounts(); - - return view('admin.users.index', ['users' => $users]); - } - - /** - * Display new user page. - * - * @return \Illuminate\View\View - */ - public function create() - { - return view('admin.users.new', [ - 'languages' => $this->getAvailableLanguages(true), - ]); - } - - /** - * Display user view page. - * - * @param \Pterodactyl\Models\User $user - * @return \Illuminate\View\View - */ - public function view(User $user) - { - return view('admin.users.view', [ - 'user' => $user, - 'languages' => $this->getAvailableLanguages(true), - ]); - } - - /** - * Delete a user from the system. - * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\User $user - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\DisplayException - */ - public function delete(Request $request, User $user) - { - if ($request->user()->id === $user->id) { - throw new DisplayException($this->translator->trans('admin/user.exceptions.user_has_servers')); - } - - $this->deletionService->handle($user); - - return redirect()->route('admin.users'); - } - - /** - * Create a user. - * - * @param \Pterodactyl\Http\Requests\Admin\UserFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - * @throws \Throwable - */ - public function store(UserFormRequest $request) - { - $user = $this->creationService->handle($request->normalize()); - $this->alert->success($this->translator->trans('admin/user.notices.account_created'))->flash(); - - return redirect()->route('admin.users.view', $user->id); - } - - /** - * Update a user on the system. - * - * @param \Pterodactyl\Http\Requests\Admin\UserFormRequest $request - * @param \Pterodactyl\Models\User $user - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(UserFormRequest $request, User $user) - { - $this->updateService->setUserLevel(User::USER_LEVEL_ADMIN); - $data = $this->updateService->handle($user, $request->normalize()); - - if (! empty($data->get('exceptions'))) { - foreach ($data->get('exceptions') as $node => $exception) { - /** @var \GuzzleHttp\Exception\RequestException $exception */ - /** @var \GuzzleHttp\Psr7\Response|null $response */ - $response = method_exists($exception, 'getResponse') ? $exception->getResponse() : null; - $message = trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ]); - - $this->alert->danger(trans('exceptions.users.node_revocation_failed', [ - 'node' => $node, - 'error' => $message, - 'link' => route('admin.nodes.view', $node), - ]))->flash(); - } - } - - $this->alert->success($this->translator->trans('admin/user.notices.account_updated'))->flash(); - - return redirect()->route('admin.users.view', $user->id); - } - - /** - * Get a JSON response of users on the system. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Database\Eloquent\Collection - */ - public function json(Request $request) - { - return $this->repository->filterUsersByQuery($request->input('q')); - } -} diff --git a/app/Http/Controllers/Api/Application/ApplicationApiController.php b/app/Http/Controllers/Api/Application/ApplicationApiController.php index bdd5f9e7b..eefd4a526 100644 --- a/app/Http/Controllers/Api/Application/ApplicationApiController.php +++ b/app/Http/Controllers/Api/Application/ApplicationApiController.php @@ -3,24 +3,16 @@ namespace Pterodactyl\Http\Controllers\Api\Application; use Illuminate\Http\Request; -use Webmozart\Assert\Assert; use Illuminate\Http\Response; +use Illuminate\Support\Collection; use Illuminate\Container\Container; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Extensions\Spatie\Fractalistic\Fractal; -use Pterodactyl\Transformers\Api\Application\BaseTransformer; abstract class ApplicationApiController extends Controller { - /** - * @var \Illuminate\Http\Request - */ - protected $request; - - /** - * @var \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal - */ - protected $fractal; + protected Fractal $fractal; + protected Request $request; /** * ApplicationApiController constructor. @@ -29,8 +21,11 @@ abstract class ApplicationApiController extends Controller { Container::getInstance()->call([$this, 'loadDependencies']); - // Parse all of the includes to use on this request. - $includes = collect(explode(',', $this->request->input('include', '')))->map(function ($value) { + // Parse all the includes to use on this request. + $input = $this->request->input('include', []); + $input = is_array($input) ? $input : explode(',', $input); + + $includes = (new Collection($input))->map(function ($value) { return trim($value); })->filter()->toArray(); @@ -41,9 +36,6 @@ abstract class ApplicationApiController extends Controller /** * Perform dependency injection of certain classes needed for core functionality * without littering the constructors of classes that extend this abstract. - * - * @param \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal $fractal - * @param \Illuminate\Http\Request $request */ public function loadDependencies(Fractal $fractal, Request $request) { @@ -52,26 +44,15 @@ abstract class ApplicationApiController extends Controller } /** - * Return an instance of an application transformer. - * - * @param string $abstract - * @return \Pterodactyl\Transformers\Api\Application\BaseTransformer + * Return an HTTP/201 response for the API. */ - public function getTransformer(string $abstract) + protected function returnAccepted(): Response { - /** @var \Pterodactyl\Transformers\Api\Application\BaseTransformer $transformer */ - $transformer = Container::getInstance()->make($abstract); - $transformer->setKey($this->request->attributes->get('api_key')); - - Assert::isInstanceOf($transformer, BaseTransformer::class); - - return $transformer; + return new Response('', Response::HTTP_ACCEPTED); } /** - * Return a HTTP/204 response for the API. - * - * @return \Illuminate\Http\Response + * Return an HTTP/204 response for the API. */ protected function returnNoContent(): Response { diff --git a/app/Http/Controllers/Api/Application/Databases/DatabaseController.php b/app/Http/Controllers/Api/Application/Databases/DatabaseController.php new file mode 100644 index 000000000..2acd2fa18 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Databases/DatabaseController.php @@ -0,0 +1,99 @@ +query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $databases = QueryBuilder::for(DatabaseHost::query()) + ->allowedFilters(['name', 'host']) + ->allowedSorts(['id', 'name', 'host']) + ->paginate($perPage); + + return $this->fractal->collection($databases) + ->transformWith(DatabaseHostTransformer::class) + ->toArray(); + } + + /** + * Returns a single database host. + */ + public function view(GetDatabaseRequest $request, DatabaseHost $databaseHost): array + { + return $this->fractal->item($databaseHost) + ->transformWith(DatabaseHostTransformer::class) + ->toArray(); + } + + /** + * Creates a new database host. + * + * @throws \Throwable + */ + public function store(StoreDatabaseRequest $request): JsonResponse + { + $databaseHost = $this->creationService->handle($request->validated()); + + return $this->fractal->item($databaseHost) + ->transformWith(DatabaseHostTransformer::class) + ->respond(JsonResponse::HTTP_CREATED); + } + + /** + * Updates a database host. + * + * @throws \Throwable + */ + public function update(UpdateDatabaseRequest $request, DatabaseHost $databaseHost): array + { + $databaseHost = $this->updateService->handle($databaseHost->id, $request->validated()); + + return $this->fractal->item($databaseHost) + ->transformWith(DatabaseHostTransformer::class) + ->toArray(); + } + + /** + * Deletes a database host. + * + * @throws \Exception + */ + public function delete(DeleteDatabaseRequest $request, DatabaseHost $databaseHost): Response + { + $databaseHost->delete(); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Application/Eggs/EggController.php b/app/Http/Controllers/Api/Application/Eggs/EggController.php new file mode 100644 index 000000000..a4003124f --- /dev/null +++ b/app/Http/Controllers/Api/Application/Eggs/EggController.php @@ -0,0 +1,118 @@ +query('per_page', '10'); + if ($perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + // @phpstan-ignore-next-line + $eggs = QueryBuilder::for(Egg::query()) + ->where('nest_id', '=', $nest->id) + ->allowedFilters(['id', 'name', 'author']) + ->allowedSorts(['id', 'name', 'author']); + if ($perPage > 0) { + $eggs = $eggs->paginate($perPage); + } + + return $this->fractal->collection($eggs) + ->transformWith(EggTransformer::class) + ->toArray(); + } + + /** + * Returns a single egg. + */ + public function view(GetEggRequest $request, Egg $egg): array + { + return $this->fractal->item($egg) + ->transformWith(EggTransformer::class) + ->toArray(); + } + + /** + * Creates a new egg. + */ + public function store(StoreEggRequest $request): JsonResponse + { + $validated = $request->validated(); + $merged = array_merge($validated, [ + 'uuid' => Uuid::uuid4()->toString(), + // TODO: allow this to be set in the request, and default to config value if null or not present. + 'author' => config('pterodactyl.service.author'), + ]); + + $egg = Egg::query()->create($merged); + + return $this->fractal->item($egg) + ->transformWith(EggTransformer::class) + ->respond(Response::HTTP_CREATED); + } + + /** + * Updates an egg. + */ + public function update(UpdateEggRequest $request, Egg $egg): array + { + $egg->update($request->validated()); + + return $this->fractal->item($egg) + ->transformWith(EggTransformer::class) + ->toArray(); + } + + /** + * Deletes an egg. + * + * @throws \Exception + */ + public function delete(DeleteEggRequest $request, Egg $egg): Response + { + $egg->delete(); + + return $this->returnNoContent(); + } + + /** + * Exports an egg. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function export(ExportEggRequest $request, int $eggId): JsonResponse + { + return new JsonResponse($this->eggExporterService->handle($eggId)); + } +} diff --git a/app/Http/Controllers/Api/Application/Eggs/EggVariableController.php b/app/Http/Controllers/Api/Application/Eggs/EggVariableController.php new file mode 100644 index 000000000..c837244d7 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Eggs/EggVariableController.php @@ -0,0 +1,75 @@ +variableCreationService->handle($egg->id, $request->validated()); + + return $this->fractal->item($variable) + ->transformWith(EggVariableTransformer::class) + ->toArray(); + } + + /** + * Updates multiple egg variables. + * + * @throws \Throwable + */ + public function update(UpdateEggVariablesRequest $request, Egg $egg): array + { + $validated = $request->validated(); + + $this->connection->transaction(function () use ($egg, $validated) { + foreach ($validated as $data) { + $this->variableUpdateService->handle($egg, $data); + } + }); + + return $this->fractal->collection($egg->refresh()->variables) + ->transformWith(EggVariableTransformer::class) + ->toArray(); + } + + /** + * Deletes a single egg variable. + */ + public function delete(Request $request, Egg $egg, EggVariable $eggVariable): Response + { + EggVariable::query() + ->where('id', $eggVariable->id) + ->where('egg_id', $egg->id) + ->delete(); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Application/Locations/LocationController.php b/app/Http/Controllers/Api/Application/Locations/LocationController.php index 9220cf358..337f5e76d 100644 --- a/app/Http/Controllers/Api/Application/Locations/LocationController.php +++ b/app/Http/Controllers/Api/Application/Locations/LocationController.php @@ -5,11 +5,12 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Locations; use Illuminate\Http\Response; use Pterodactyl\Models\Location; use Illuminate\Http\JsonResponse; +use Spatie\QueryBuilder\QueryBuilder; use Pterodactyl\Services\Locations\LocationUpdateService; use Pterodactyl\Services\Locations\LocationCreationService; use Pterodactyl\Services\Locations\LocationDeletionService; -use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Transformers\Api\Application\LocationTransformer; +use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationRequest; use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationsRequest; @@ -19,83 +20,51 @@ use Pterodactyl\Http\Requests\Api\Application\Locations\UpdateLocationRequest; class LocationController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Locations\LocationCreationService - */ - private $creationService; - - /** - * @var \Pterodactyl\Services\Locations\LocationDeletionService - */ - private $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Services\Locations\LocationUpdateService - */ - private $updateService; - /** * LocationController constructor. - * - * @param \Pterodactyl\Services\Locations\LocationCreationService $creationService - * @param \Pterodactyl\Services\Locations\LocationDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository - * @param \Pterodactyl\Services\Locations\LocationUpdateService $updateService */ public function __construct( - LocationCreationService $creationService, - LocationDeletionService $deletionService, - LocationRepositoryInterface $repository, - LocationUpdateService $updateService + private LocationCreationService $creationService, + private LocationDeletionService $deletionService, + private LocationUpdateService $updateService ) { parent::__construct(); - - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->repository = $repository; - $this->updateService = $updateService; } /** - * Return all of the locations currently registered on the Panel. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationsRequest $request - * @return array + * Return all the locations currently registered on the Panel. */ public function index(GetLocationsRequest $request): array { - $locations = $this->repository->setSearchTerm($request->input('search'))->paginated(50); + $perPage = (int) $request->query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $locations = QueryBuilder::for(Location::query()) + ->allowedFilters(['short', 'long']) + ->allowedSorts(['id', 'short', 'long']) + ->paginate($perPage); return $this->fractal->collection($locations) - ->transformWith($this->getTransformer(LocationTransformer::class)) + ->transformWith(LocationTransformer::class) ->toArray(); } /** * Return a single location. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationRequest $request - * @return array */ - public function view(GetLocationRequest $request): array + public function view(GetLocationRequest $request, Location $location): array { - return $this->fractal->item($request->getModel(Location::class)) - ->transformWith($this->getTransformer(LocationTransformer::class)) + return $this->fractal->item($location) + ->transformWith(LocationTransformer::class) ->toArray(); } /** - * Store a new location on the Panel and return a HTTP/201 response code with the + * Store a new location on the Panel and return an HTTP/201 response code with the * new location attached. * - * @param \Pterodactyl\Http\Requests\Api\Application\Locations\StoreLocationRequest $request - * @return \Illuminate\Http\JsonResponse - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ public function store(StoreLocationRequest $request): JsonResponse @@ -103,45 +72,34 @@ class LocationController extends ApplicationApiController $location = $this->creationService->handle($request->validated()); return $this->fractal->item($location) - ->transformWith($this->getTransformer(LocationTransformer::class)) - ->addMeta([ - 'resource' => route('api.application.locations.view', [ - 'location' => $location->id, - ]), - ]) + ->transformWith(LocationTransformer::class) ->respond(201); } /** * Update a location on the Panel and return the updated record to the user. * - * @param \Pterodactyl\Http\Requests\Api\Application\Locations\UpdateLocationRequest $request - * @return array - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update(UpdateLocationRequest $request): array + public function update(UpdateLocationRequest $request, Location $location): array { - $location = $this->updateService->handle($request->getModel(Location::class), $request->validated()); + $location = $this->updateService->handle($location, $request->validated()); return $this->fractal->item($location) - ->transformWith($this->getTransformer(LocationTransformer::class)) + ->transformWith(LocationTransformer::class) ->toArray(); } /** * Delete a location from the Panel. * - * @param \Pterodactyl\Http\Requests\Api\Application\Locations\DeleteLocationRequest $request - * @return \Illuminate\Http\Response - * * @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException */ - public function delete(DeleteLocationRequest $request): Response + public function delete(DeleteLocationRequest $request, Location $location): Response { - $this->deletionService->handle($request->getModel(Location::class)); + $this->deletionService->handle($location); - return response('', 204); + return $this->returnNoContent(); } } diff --git a/app/Http/Controllers/Api/Application/Mounts/MountController.php b/app/Http/Controllers/Api/Application/Mounts/MountController.php new file mode 100644 index 000000000..6606004fd --- /dev/null +++ b/app/Http/Controllers/Api/Application/Mounts/MountController.php @@ -0,0 +1,163 @@ +query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $mounts = QueryBuilder::for(Mount::query()) + ->allowedFilters(['id', 'name', 'source', 'target']) + ->allowedSorts(['id', 'name', 'source', 'target']) + ->paginate($perPage); + + return $this->fractal->collection($mounts) + ->transformWith(MountTransformer::class) + ->toArray(); + } + + /** + * Returns a single mount. + */ + public function view(GetMountRequest $request, Mount $mount): array + { + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->toArray(); + } + + /** + * Creates a new mount. + */ + public function store(StoreMountRequest $request): JsonResponse + { + $mount = Mount::query()->create($request->validated()); + + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->respond(JsonResponse::HTTP_CREATED); + } + + /** + * Updates a mount. + */ + public function update(UpdateMountRequest $request, Mount $mount): array + { + $mount->update($request->validated()); + + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->toArray(); + } + + /** + * Deletes a mount. + * + * @throws \Exception + */ + public function delete(DeleteMountRequest $request, Mount $mount): Response + { + $mount->delete(); + + return $this->returnNoContent(); + } + + /** + * Attaches eggs to a mount. + */ + public function addEggs(MountEggsRequest $request, Mount $mount): array + { + $data = $request->validated(); + + $eggs = $data['eggs'] ?? []; + if (count($eggs) > 0) { + $mount->eggs()->syncWithoutDetaching($eggs); + } + + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->toArray(); + } + + /** + * Attaches nodes to a mount. + */ + public function addNodes(MountNodesRequest $request, Mount $mount): array + { + $data = $request->validated(); + + $nodes = $data['nodes'] ?? []; + if (count($nodes) > 0) { + $mount->nodes()->syncWithoutDetaching($nodes); + } + + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->toArray(); + } + + /** + * Detaches eggs from a mount. + */ + public function deleteEggs(MountEggsRequest $request, Mount $mount): array + { + $data = $request->validated(); + + $eggs = $data['eggs'] ?? []; + if (count($eggs) > 0) { + $mount->eggs()->detach($eggs); + } + + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->toArray(); + } + + /** + * Detaches nodes from a mount. + */ + public function deleteNodes(MountNodesRequest $request, Mount $mount): array + { + $data = $request->validated(); + + $nodes = $data['nodes'] ?? []; + if (count($nodes) > 0) { + $mount->nodes()->detach($nodes); + } + + return $this->fractal->item($mount) + ->transformWith(MountTransformer::class) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Nests/EggController.php b/app/Http/Controllers/Api/Application/Nests/EggController.php deleted file mode 100644 index 21ce4ec9f..000000000 --- a/app/Http/Controllers/Api/Application/Nests/EggController.php +++ /dev/null @@ -1,61 +0,0 @@ -repository = $repository; - } - - /** - * Return all eggs that exist for a given nest. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Nests\Eggs\GetEggsRequest $request - * @return array - */ - public function index(GetEggsRequest $request): array - { - $eggs = $this->repository->findWhere([ - ['nest_id', '=', $request->getModel(Nest::class)->id], - ]); - - return $this->fractal->collection($eggs) - ->transformWith($this->getTransformer(EggTransformer::class)) - ->toArray(); - } - - /** - * Return a single egg that exists on the specified nest. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Nests\Eggs\GetEggRequest $request - * @return array - */ - public function view(GetEggRequest $request): array - { - return $this->fractal->item($request->getModel(Egg::class)) - ->transformWith($this->getTransformer(EggTransformer::class)) - ->toArray(); - } -} diff --git a/app/Http/Controllers/Api/Application/Nests/NestController.php b/app/Http/Controllers/Api/Application/Nests/NestController.php index adeacc56c..708f59195 100644 --- a/app/Http/Controllers/Api/Application/Nests/NestController.php +++ b/app/Http/Controllers/Api/Application/Nests/NestController.php @@ -3,55 +3,123 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Nests; use Pterodactyl\Models\Nest; -use Pterodactyl\Contracts\Repository\NestRepositoryInterface; +use Illuminate\Http\Response; +use Spatie\QueryBuilder\QueryBuilder; +use Pterodactyl\Services\Nests\NestUpdateService; +use Pterodactyl\Services\Nests\NestCreationService; +use Pterodactyl\Services\Nests\NestDeletionService; +use Pterodactyl\Services\Eggs\Sharing\EggImporterService; +use Pterodactyl\Transformers\Api\Application\EggTransformer; use Pterodactyl\Transformers\Api\Application\NestTransformer; +use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException; +use Pterodactyl\Http\Requests\Api\Application\Nests\GetNestRequest; +use Pterodactyl\Http\Requests\Api\Application\Eggs\ImportEggRequest; use Pterodactyl\Http\Requests\Api\Application\Nests\GetNestsRequest; +use Pterodactyl\Http\Requests\Api\Application\Nests\StoreNestRequest; +use Pterodactyl\Http\Requests\Api\Application\Nests\DeleteNestRequest; +use Pterodactyl\Http\Requests\Api\Application\Nests\UpdateNestRequest; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; class NestController extends ApplicationApiController { - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - private $repository; - /** * NestController constructor. - * - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $repository */ - public function __construct(NestRepositoryInterface $repository) - { + public function __construct( + private NestCreationService $nestCreationService, + private NestDeletionService $nestDeletionService, + private NestUpdateService $nestUpdateService, + private EggImporterService $eggImporterService + ) { parent::__construct(); - - $this->repository = $repository; } /** * Return all Nests that exist on the Panel. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Nests\GetNestsRequest $request - * @return array */ public function index(GetNestsRequest $request): array { - $nests = $this->repository->paginated(50); + $perPage = (int) $request->query('per_page', '10'); + if ($perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $nests = QueryBuilder::for(Nest::query()) + ->allowedFilters(['id', 'name', 'author']) + ->allowedSorts(['id', 'name', 'author']); + if ($perPage > 0) { + $nests = $nests->paginate($perPage); + } return $this->fractal->collection($nests) - ->transformWith($this->getTransformer(NestTransformer::class)) + ->transformWith(NestTransformer::class) ->toArray(); } /** * Return information about a single Nest model. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Nests\GetNestsRequest $request - * @return array */ - public function view(GetNestsRequest $request): array + public function view(GetNestRequest $request, Nest $nest): array { - return $this->fractal->item($request->getModel(Nest::class)) - ->transformWith($this->getTransformer(NestTransformer::class)) + return $this->fractal->item($nest) + ->transformWith(NestTransformer::class) ->toArray(); } + + /** + * Creates a new nest. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreNestRequest $request): array + { + $nest = $this->nestCreationService->handle($request->validated()); + + return $this->fractal->item($nest) + ->transformWith(NestTransformer::class) + ->toArray(); + } + + /** + * Imports an egg. + */ + public function import(ImportEggRequest $request, Nest $nest): array + { + $egg = $this->eggImporterService->handleContent( + $nest->id, + $request->getContent(), + $request->headers->get('Content-Type'), + ); + + return $this->fractal->item($egg) + ->transformWith(EggTransformer::class) + ->toArray(); + } + + /** + * Updates an existing nest. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateNestRequest $request, Nest $nest): array + { + $this->nestUpdateService->handle($nest->id, $request->validated()); + + return $this->fractal->item($nest) + ->transformWith(NestTransformer::class) + ->toArray(); + } + + /** + * Deletes an existing nest. + * + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException + */ + public function delete(DeleteNestRequest $request, Nest $nest): Response + { + $this->nestDeletionService->handle($nest->id); + + return $this->returnNoContent(); + } } diff --git a/app/Http/Controllers/Api/Application/Nodes/AllocationController.php b/app/Http/Controllers/Api/Application/Nodes/AllocationController.php index 094834fe8..41483bf41 100644 --- a/app/Http/Controllers/Api/Application/Nodes/AllocationController.php +++ b/app/Http/Controllers/Api/Application/Nodes/AllocationController.php @@ -5,9 +5,12 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Nodes; use Pterodactyl\Models\Node; use Illuminate\Http\Response; use Pterodactyl\Models\Allocation; +use Spatie\QueryBuilder\QueryBuilder; +use Spatie\QueryBuilder\AllowedFilter; +use Illuminate\Database\Eloquent\Builder; use Pterodactyl\Services\Allocations\AssignmentService; use Pterodactyl\Services\Allocations\AllocationDeletionService; -use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; +use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException; use Pterodactyl\Transformers\Api\Application\AllocationTransformer; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Requests\Api\Application\Allocations\GetAllocationsRequest; @@ -16,87 +19,70 @@ use Pterodactyl\Http\Requests\Api\Application\Allocations\DeleteAllocationReques class AllocationController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Allocations\AssignmentService - */ - private $assignmentService; - - /** - * @var \Pterodactyl\Services\Allocations\AllocationDeletionService - */ - private $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - private $repository; - /** * AllocationController constructor. - * - * @param \Pterodactyl\Services\Allocations\AssignmentService $assignmentService - * @param \Pterodactyl\Services\Allocations\AllocationDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository */ public function __construct( - AssignmentService $assignmentService, - AllocationDeletionService $deletionService, - AllocationRepositoryInterface $repository + private AssignmentService $assignmentService, + private AllocationDeletionService $deletionService ) { parent::__construct(); - - $this->assignmentService = $assignmentService; - $this->deletionService = $deletionService; - $this->repository = $repository; } /** - * Return all of the allocations that exist for a given node. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Allocations\GetAllocationsRequest $request - * @return array + * Return all the allocations that exist for a given node. */ - public function index(GetAllocationsRequest $request): array + public function index(GetAllocationsRequest $request, Node $node): array { - $allocations = $this->repository->getPaginatedAllocationsForNode( - $request->getModel(Node::class)->id, 50 - ); + $perPage = (int) $request->query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $allocations = QueryBuilder::for(Allocation::query()->where('node_id', '=', $node->id)) + ->allowedFilters([ + 'id', 'ip', 'port', 'alias', + AllowedFilter::callback('server_id', function (Builder $query, $value) { + if ($value === '0') { + $query->whereNull('server_id'); + } else { + $query->where('server_id', '=', $value); + } + }), + ]) + ->allowedSorts(['id', 'ip', 'port', 'server_id']) + ->paginate($perPage); return $this->fractal->collection($allocations) - ->transformWith($this->getTransformer(AllocationTransformer::class)) + ->transformWith(AllocationTransformer::class) ->toArray(); } /** * Store new allocations for a given node. * - * @param \Pterodactyl\Http\Requests\Api\Application\Allocations\StoreAllocationRequest $request - * @return \Illuminate\Http\Response - * + * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException */ - public function store(StoreAllocationRequest $request): Response + public function store(StoreAllocationRequest $request, Node $node): Response { - $this->assignmentService->handle($request->getModel(Node::class), $request->validated()); + $this->assignmentService->handle($node, $request->validated()); - return response('', 204); + return $this->returnNoContent(); } /** * Delete a specific allocation from the Panel. * - * @param \Pterodactyl\Http\Requests\Api\Application\Allocations\DeleteAllocationRequest $request - * @return \Illuminate\Http\Response - * * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException */ - public function delete(DeleteAllocationRequest $request): Response + public function delete(DeleteAllocationRequest $request, Node $node, Allocation $allocation): Response { - $this->deletionService->handle($request->getModel(Allocation::class)); + $this->deletionService->handle($allocation); - return response('', 204); + return $this->returnNoContent(); } } diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php b/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php new file mode 100644 index 000000000..4709e109a --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeConfigurationController.php @@ -0,0 +1,25 @@ +query('format') === 'yaml') { + return $node->getYamlConfiguration(); + } + + return new JsonResponse($node->getConfiguration()); + } +} diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeController.php b/app/Http/Controllers/Api/Application/Nodes/NodeController.php index 2ae2b9603..c30272d07 100644 --- a/app/Http/Controllers/Api/Application/Nodes/NodeController.php +++ b/app/Http/Controllers/Api/Application/Nodes/NodeController.php @@ -5,11 +5,12 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Nodes; use Pterodactyl\Models\Node; use Illuminate\Http\Response; use Illuminate\Http\JsonResponse; +use Spatie\QueryBuilder\QueryBuilder; use Pterodactyl\Services\Nodes\NodeUpdateService; use Pterodactyl\Services\Nodes\NodeCreationService; use Pterodactyl\Services\Nodes\NodeDeletionService; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Transformers\Api\Application\NodeTransformer; +use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException; use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest; use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodesRequest; use Pterodactyl\Http\Requests\Api\Application\Nodes\StoreNodeRequest; @@ -19,83 +20,51 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; class NodeController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Nodes\NodeCreationService - */ - private $creationService; - - /** - * @var \Pterodactyl\Services\Nodes\NodeDeletionService - */ - private $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Services\Nodes\NodeUpdateService - */ - private $updateService; - /** * NodeController constructor. - * - * @param \Pterodactyl\Services\Nodes\NodeCreationService $creationService - * @param \Pterodactyl\Services\Nodes\NodeDeletionService $deletionService - * @param \Pterodactyl\Services\Nodes\NodeUpdateService $updateService - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository */ public function __construct( - NodeCreationService $creationService, - NodeDeletionService $deletionService, - NodeUpdateService $updateService, - NodeRepositoryInterface $repository + private NodeCreationService $creationService, + private NodeDeletionService $deletionService, + private NodeUpdateService $updateService ) { parent::__construct(); - - $this->repository = $repository; - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->updateService = $updateService; } /** - * Return all of the nodes currently available on the Panel. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodesRequest $request - * @return array + * Return all the nodes currently available on the Panel. */ public function index(GetNodesRequest $request): array { - $nodes = $this->repository->setSearchTerm($request->input('search'))->paginated(50); + $perPage = (int) $request->query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $nodes = QueryBuilder::for(Node::query()) + ->allowedFilters(['id', 'uuid', 'name', 'fqdn', 'daemon_token_id']) + ->allowedSorts(['id', 'uuid', 'name', 'location_id', 'fqdn', 'memory', 'disk']) + ->paginate($perPage); return $this->fractal->collection($nodes) - ->transformWith($this->getTransformer(NodeTransformer::class)) + ->transformWith(NodeTransformer::class) ->toArray(); } /** * Return data for a single instance of a node. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest $request - * @return array */ - public function view(GetNodeRequest $request): array + public function view(GetNodeRequest $request, Node $node): array { - return $this->fractal->item($request->getModel(Node::class)) - ->transformWith($this->getTransformer(NodeTransformer::class)) + return $this->fractal->item($node) + ->transformWith(NodeTransformer::class) ->toArray(); } /** - * Create a new node on the Panel. Returns the created node and a HTTP/201 + * Create a new node on the Panel. Returns the created node and an HTTP/201 * status response on success. * - * @param \Pterodactyl\Http\Requests\Api\Application\Nodes\StoreNodeRequest $request - * @return \Illuminate\Http\JsonResponse - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ public function store(StoreNodeRequest $request): JsonResponse @@ -103,33 +72,24 @@ class NodeController extends ApplicationApiController $node = $this->creationService->handle($request->validated()); return $this->fractal->item($node) - ->transformWith($this->getTransformer(NodeTransformer::class)) - ->addMeta([ - 'resource' => route('api.application.nodes.view', [ - 'node' => $node->id, - ]), - ]) + ->transformWith(NodeTransformer::class) ->respond(201); } /** * Update an existing node on the Panel. * - * @param \Pterodactyl\Http\Requests\Api\Application\Nodes\UpdateNodeRequest $request - * @return array - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function update(UpdateNodeRequest $request): array + public function update(UpdateNodeRequest $request, Node $node): array { $node = $this->updateService->handle( - $request->getModel(Node::class), $request->validated(), $request->input('reset_secret') === true + $node, + $request->validated(), ); return $this->fractal->item($node) - ->transformWith($this->getTransformer(NodeTransformer::class)) + ->transformWith(NodeTransformer::class) ->toArray(); } @@ -137,15 +97,12 @@ class NodeController extends ApplicationApiController * Deletes a given node from the Panel as long as there are no servers * currently attached to it. * - * @param \Pterodactyl\Http\Requests\Api\Application\Nodes\DeleteNodeRequest $request - * @return \Illuminate\Http\Response - * * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException */ - public function delete(DeleteNodeRequest $request): Response + public function delete(DeleteNodeRequest $request, Node $node): Response { - $this->deletionService->handle($request->getModel(Node::class)); + $this->deletionService->handle($node); - return response('', 204); + return $this->returnNoContent(); } } diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php new file mode 100644 index 000000000..c3e9303a7 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php @@ -0,0 +1,39 @@ +validated(); + $nodes = $this->viableNodesService->setLocations($data['location_ids'] ?? []) + ->setMemory($data['memory']) + ->setDisk($data['disk']) + ->handle($request->query('per_page'), $request->query('page')); + + return $this->fractal->collection($nodes) + ->transformWith(NodeTransformer::class) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeInformationController.php b/app/Http/Controllers/Api/Application/Nodes/NodeInformationController.php new file mode 100644 index 000000000..ff099e15b --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeInformationController.php @@ -0,0 +1,47 @@ +cache + ->tags(['nodes']) + ->remember($node->uuid, Carbon::now()->addSeconds(30), function () use ($node) { + return $this->repository->setNode($node)->getSystemInformation(); + }); + + return new JsonResponse([ + 'version' => $data['version'] ?? null, + 'system' => [ + 'type' => Str::title($data['os'] ?? 'Unknown'), + 'arch' => $data['architecture'] ?? null, + 'release' => $data['kernel_version'] ?? null, + 'cpus' => $data['cpu_count'] ?? null, + ], + ]); + } +} diff --git a/app/Http/Controllers/Api/Application/Roles/RoleController.php b/app/Http/Controllers/Api/Application/Roles/RoleController.php new file mode 100644 index 000000000..21ec404aa --- /dev/null +++ b/app/Http/Controllers/Api/Application/Roles/RoleController.php @@ -0,0 +1,96 @@ +query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $roles = QueryBuilder::for(AdminRole::query()) + ->allowedFilters(['id', 'name']) + ->allowedSorts(['id', 'name']) + ->paginate($perPage); + + return $this->fractal->collection($roles) + ->transformWith(AdminRoleTransformer::class) + ->toArray(); + } + + /** + * Returns a single role. + */ + public function view(GetRoleRequest $request, AdminRole $role): array + { + return $this->fractal->item($role) + ->transformWith(AdminRoleTransformer::class) + ->toArray(); + } + + /** + * Creates a new role. + */ + public function store(StoreRoleRequest $request): JsonResponse + { + $data = array_merge($request->validated(), [ + 'sort_id' => 99, + ]); + $role = AdminRole::query()->create($data); + + return $this->fractal->item($role) + ->transformWith(AdminRoleTransformer::class) + ->respond(JsonResponse::HTTP_CREATED); + } + + /** + * Updates a role. + */ + public function update(UpdateRoleRequest $request, AdminRole $role): array + { + $role->update($request->validated()); + + return $this->fractal->item($role) + ->transformWith(AdminRoleTransformer::class) + ->toArray(); + } + + /** + * Deletes a role. + * + * @throws \Exception + */ + public function delete(DeleteRoleRequest $request, AdminRole $role): Response + { + $role->delete(); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Controllers/Api/Application/Servers/DatabaseController.php b/app/Http/Controllers/Api/Application/Servers/DatabaseController.php index a2030bbc7..21f0da45f 100644 --- a/app/Http/Controllers/Api/Application/Servers/DatabaseController.php +++ b/app/Http/Controllers/Api/Application/Servers/DatabaseController.php @@ -8,7 +8,6 @@ use Pterodactyl\Models\Database; use Illuminate\Http\JsonResponse; use Pterodactyl\Services\Databases\DatabasePasswordService; use Pterodactyl\Services\Databases\DatabaseManagementService; -use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Transformers\Api\Application\ServerDatabaseTransformer; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabaseRequest; @@ -18,120 +17,74 @@ use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\StoreServerDatab class DatabaseController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Databases\DatabaseManagementService - */ - private $databaseManagementService; - - /** - * @var \Pterodactyl\Services\Databases\DatabasePasswordService - */ - private $databasePasswordService; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - private $repository; - /** * DatabaseController constructor. - * - * @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService - * @param \Pterodactyl\Services\Databases\DatabasePasswordService $databasePasswordService - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository */ public function __construct( - DatabaseManagementService $databaseManagementService, - DatabasePasswordService $databasePasswordService, - DatabaseRepositoryInterface $repository + private DatabaseManagementService $databaseManagementService, + private DatabasePasswordService $databasePasswordService ) { parent::__construct(); - - $this->databaseManagementService = $databaseManagementService; - $this->databasePasswordService = $databasePasswordService; - $this->repository = $repository; } /** * Return a listing of all databases currently available to a single * server. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabasesRequest $request - * @return array */ - public function index(GetServerDatabasesRequest $request): array + public function index(GetServerDatabasesRequest $request, Server $server): array { - $databases = $this->repository->getDatabasesForServer($request->getModel(Server::class)->id); - - return $this->fractal->collection($databases) - ->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) + return $this->fractal->collection($server->databases) + ->transformWith(ServerDatabaseTransformer::class) ->toArray(); } /** * Return a single server database. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabaseRequest $request - * @return array */ - public function view(GetServerDatabaseRequest $request): array + public function view(GetServerDatabaseRequest $request, Server $server, Database $database): array { - return $this->fractal->item($request->getModel(Database::class)) - ->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) + return $this->fractal->item($database) + ->transformWith(ServerDatabaseTransformer::class) ->toArray(); } /** * Reset the password for a specific server database. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest $request - * @return \Illuminate\Http\Response - * * @throws \Throwable */ - public function resetPassword(ServerDatabaseWriteRequest $request): Response + public function resetPassword(ServerDatabaseWriteRequest $request, Server $server, Database $database): Response { - $this->databasePasswordService->handle($request->getModel(Database::class)); + $this->databasePasswordService->handle($database); - return response('', 204); + return $this->returnNoContent(); } /** * Create a new database on the Panel for a given server. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\StoreServerDatabaseRequest $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Exception + * @throws \Throwable */ - public function store(StoreServerDatabaseRequest $request): JsonResponse + public function store(StoreServerDatabaseRequest $request, Server $server): JsonResponse { - $server = $request->getModel(Server::class); - $database = $this->databaseManagementService->create($server->id, $request->validated()); + $database = $this->databaseManagementService->create($server, array_merge($request->validated(), [ + 'database' => $request->databaseName(), + ])); return $this->fractal->item($database) - ->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) - ->addMeta([ - 'resource' => route('api.application.servers.databases.view', [ - 'server' => $server->id, - 'database' => $database->id, - ]), - ]) - ->respond(201); + ->transformWith(ServerDatabaseTransformer::class) + ->respond(Response::HTTP_CREATED); } /** * Handle a request to delete a specific server database from the Panel. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Exception */ - public function delete(ServerDatabaseWriteRequest $request): Response + public function delete(ServerDatabaseWriteRequest $request, Database $database): Response { - $this->databaseManagementService->delete($request->getModel(Database::class)->id); + $this->databaseManagementService->delete($database); - return response('', 204); + return $this->returnNoContent(); } } diff --git a/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php b/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php index 391c5645c..3f6a3624c 100644 --- a/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php +++ b/app/Http/Controllers/Api/Application/Servers/ExternalServerController.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Servers; +use Pterodactyl\Models\Server; use Pterodactyl\Transformers\Api\Application\ServerTransformer; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Requests\Api\Application\Servers\GetExternalServerRequest; @@ -10,14 +11,13 @@ class ExternalServerController extends ApplicationApiController { /** * Retrieve a specific server from the database using its external ID. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\GetExternalServerRequest $request - * @return array */ - public function index(GetExternalServerRequest $request): array + public function index(GetExternalServerRequest $request, string $external_id): array { - return $this->fractal->item($request->getServerModel()) - ->transformWith($this->getTransformer(ServerTransformer::class)) + $server = Server::query()->where('external_id', $external_id)->firstOrFail(); + + return $this->fractal->item($server) + ->transformWith(ServerTransformer::class) ->toArray(); } } diff --git a/app/Http/Controllers/Api/Application/Servers/ServerController.php b/app/Http/Controllers/Api/Application/Servers/ServerController.php index 1ef1e0b62..ef633d46b 100644 --- a/app/Http/Controllers/Api/Application/Servers/ServerController.php +++ b/app/Http/Controllers/Api/Application/Servers/ServerController.php @@ -5,110 +5,88 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Servers; use Illuminate\Http\Response; use Pterodactyl\Models\Server; use Illuminate\Http\JsonResponse; +use Spatie\QueryBuilder\QueryBuilder; use Pterodactyl\Services\Servers\ServerCreationService; use Pterodactyl\Services\Servers\ServerDeletionService; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Services\Servers\BuildModificationService; +use Pterodactyl\Services\Servers\DetailsModificationService; use Pterodactyl\Transformers\Api\Application\ServerTransformer; +use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException; use Pterodactyl\Http\Requests\Api\Application\Servers\GetServerRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\GetServersRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\StoreServerRequest; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; +use Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerRequest; class ServerController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Servers\ServerCreationService - */ - private $creationService; - - /** - * @var \Pterodactyl\Services\Servers\ServerDeletionService - */ - private $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - /** * ServerController constructor. - * - * @param \Pterodactyl\Services\Servers\ServerCreationService $creationService - * @param \Pterodactyl\Services\Servers\ServerDeletionService $deletionService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ public function __construct( - ServerCreationService $creationService, - ServerDeletionService $deletionService, - ServerRepositoryInterface $repository + private BuildModificationService $buildModificationService, + private DetailsModificationService $detailsModificationService, + private ServerCreationService $creationService, + private ServerDeletionService $deletionService ) { parent::__construct(); - - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->repository = $repository; } /** - * Return all of the servers that currently exist on the Panel. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\GetServersRequest $request - * @return array + * Return all the servers that currently exist on the Panel. */ public function index(GetServersRequest $request): array { - $servers = $this->repository->setSearchTerm($request->input('search'))->paginated(50); + $perPage = (int) $request->query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $servers = QueryBuilder::for(Server::query()) + ->allowedFilters(['id', 'uuid', 'uuidShort', 'name', 'owner_id', 'node_id', 'external_id']) + ->allowedSorts(['id', 'uuid', 'uuidShort', 'name', 'owner_id', 'node_id', 'status']) + ->paginate($perPage); return $this->fractal->collection($servers) - ->transformWith($this->getTransformer(ServerTransformer::class)) + ->transformWith(ServerTransformer::class) ->toArray(); } /** * Create a new server on the system. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\StoreServerRequest $request - * @return \Illuminate\Http\JsonResponse - * + * @throws \Throwable * @throws \Illuminate\Validation\ValidationException * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException */ public function store(StoreServerRequest $request): JsonResponse { - $server = $this->creationService->handle($request->validated(), $request->getDeploymentObject()); + $server = $this->creationService->handle($request->validated()); return $this->fractal->item($server) - ->transformWith($this->getTransformer(ServerTransformer::class)) - ->respond(201); + ->transformWith(ServerTransformer::class) + ->respond(Response::HTTP_CREATED); } /** * Show a single server transformed for the application API. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\GetServerRequest $request - * @return array */ - public function view(GetServerRequest $request): array + public function view(GetServerRequest $request, Server $server): array { - return $this->fractal->item($request->getModel(Server::class)) - ->transformWith($this->getTransformer(ServerTransformer::class)) + return $this->fractal->item($server) + ->transformWith(ServerTransformer::class) ->toArray(); } /** - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request - * @param \Pterodactyl\Models\Server $server - * @param string $force - * @return \Illuminate\Http\Response + * Deletes a server. * * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Throwable */ public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response { @@ -116,4 +94,24 @@ class ServerController extends ApplicationApiController return $this->returnNoContent(); } + + /** + * Update a server. + * + * @throws \Throwable + * @throws \Illuminate\Validation\ValidationException + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException + */ + public function update(UpdateServerRequest $request, Server $server): array + { + $server = $this->buildModificationService->handle($server, $request->validated()); + $server = $this->detailsModificationService->returnUpdatedModel()->handle($server, $request->validated()); + + return $this->fractal->item($server) + ->transformWith(ServerTransformer::class) + ->toArray(); + } } diff --git a/app/Http/Controllers/Api/Application/Servers/ServerDetailsController.php b/app/Http/Controllers/Api/Application/Servers/ServerDetailsController.php deleted file mode 100644 index e544c138a..000000000 --- a/app/Http/Controllers/Api/Application/Servers/ServerDetailsController.php +++ /dev/null @@ -1,80 +0,0 @@ -buildModificationService = $buildModificationService; - $this->detailsModificationService = $detailsModificationService; - } - - /** - * Update the details for a specific server. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerDetailsRequest $request - * @return array - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function details(UpdateServerDetailsRequest $request): array - { - $server = $this->detailsModificationService->returnUpdatedModel()->handle( - $request->getModel(Server::class), $request->validated() - ); - - return $this->fractal->item($server) - ->transformWith($this->getTransformer(ServerTransformer::class)) - ->toArray(); - } - - /** - * Update the build details for a specific server. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerBuildConfigurationRequest $request - * @return array - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function build(UpdateServerBuildConfigurationRequest $request): array - { - $server = $this->buildModificationService->handle($request->getModel(Server::class), $request->validated()); - - return $this->fractal->item($server) - ->transformWith($this->getTransformer(ServerTransformer::class)) - ->toArray(); - } -} diff --git a/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php index 9ab324d7e..8e20d098b 100644 --- a/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php +++ b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php @@ -6,59 +6,29 @@ use Illuminate\Http\Response; use Pterodactyl\Models\Server; use Pterodactyl\Services\Servers\SuspensionService; use Pterodactyl\Services\Servers\ReinstallServerService; -use Pterodactyl\Services\Servers\ContainerRebuildService; use Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; class ServerManagementController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Servers\ContainerRebuildService - */ - private $rebuildService; - - /** - * @var \Pterodactyl\Services\Servers\ReinstallServerService - */ - private $reinstallServerService; - - /** - * @var \Pterodactyl\Services\Servers\SuspensionService - */ - private $suspensionService; - /** * SuspensionController constructor. - * - * @param \Pterodactyl\Services\Servers\ContainerRebuildService $rebuildService - * @param \Pterodactyl\Services\Servers\ReinstallServerService $reinstallServerService - * @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService */ public function __construct( - ContainerRebuildService $rebuildService, - ReinstallServerService $reinstallServerService, - SuspensionService $suspensionService + private ReinstallServerService $reinstallServerService, + private SuspensionService $suspensionService ) { parent::__construct(); - - $this->rebuildService = $rebuildService; - $this->reinstallServerService = $reinstallServerService; - $this->suspensionService = $suspensionService; } /** * Suspend a server on the Panel. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function suspend(ServerWriteRequest $request): Response + public function suspend(ServerWriteRequest $request, Server $server): Response { - $this->suspensionService->toggle($request->getModel(Server::class), SuspensionService::ACTION_SUSPEND); + $this->suspensionService->toggle($server); return $this->returnNoContent(); } @@ -66,16 +36,11 @@ class ServerManagementController extends ApplicationApiController /** * Unsuspend a server on the Panel. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function unsuspend(ServerWriteRequest $request): Response + public function unsuspend(ServerWriteRequest $request, Server $server): Response { - $this->suspensionService->toggle($request->getModel(Server::class), SuspensionService::ACTION_UNSUSPEND); + $this->suspensionService->toggle($server, SuspensionService::ACTION_UNSUSPEND); return $this->returnNoContent(); } @@ -83,31 +48,11 @@ class ServerManagementController extends ApplicationApiController /** * Mark a server as needing to be reinstalled. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function reinstall(ServerWriteRequest $request): Response + public function reinstall(ServerWriteRequest $request, Server $server): Response { - $this->reinstallServerService->reinstall($request->getModel(Server::class)); - - return $this->returnNoContent(); - } - - /** - * Mark a server as needing its container rebuilt the next time it is started. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - */ - public function rebuild(ServerWriteRequest $request): Response - { - $this->rebuildService->handle($request->getModel(Server::class)); + $this->reinstallServerService->handle($server); return $this->returnNoContent(); } diff --git a/app/Http/Controllers/Api/Application/Servers/StartupController.php b/app/Http/Controllers/Api/Application/Servers/StartupController.php index 0265af464..ea6c28ff0 100644 --- a/app/Http/Controllers/Api/Application/Servers/StartupController.php +++ b/app/Http/Controllers/Api/Application/Servers/StartupController.php @@ -11,42 +11,27 @@ use Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerStartupRequest class StartupController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Servers\StartupModificationService - */ - private $modificationService; - /** * StartupController constructor. - * - * @param \Pterodactyl\Services\Servers\StartupModificationService $modificationService */ - public function __construct(StartupModificationService $modificationService) + public function __construct(private StartupModificationService $modificationService) { parent::__construct(); - - $this->modificationService = $modificationService; } /** * Update the startup and environment settings for a specific server. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerStartupRequest $request - * @return array - * - * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function index(UpdateServerStartupRequest $request): array + public function index(UpdateServerStartupRequest $request, Server $server): array { $server = $this->modificationService ->setUserLevel(User::USER_LEVEL_ADMIN) - ->handle($request->getModel(Server::class), $request->validated()); + ->handle($server, $request->validated()); return $this->fractal->item($server) - ->transformWith($this->getTransformer(ServerTransformer::class)) + ->transformWith(ServerTransformer::class) ->toArray(); } } diff --git a/app/Http/Controllers/Api/Application/Users/ExternalUserController.php b/app/Http/Controllers/Api/Application/Users/ExternalUserController.php index f58138173..380c0cc3b 100644 --- a/app/Http/Controllers/Api/Application/Users/ExternalUserController.php +++ b/app/Http/Controllers/Api/Application/Users/ExternalUserController.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Users; +use Pterodactyl\Models\User; use Pterodactyl\Transformers\Api\Application\UserTransformer; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Requests\Api\Application\Users\GetExternalUserRequest; @@ -10,14 +11,13 @@ class ExternalUserController extends ApplicationApiController { /** * Retrieve a specific user from the database using their external ID. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Users\GetExternalUserRequest $request - * @return array */ - public function index(GetExternalUserRequest $request): array + public function index(GetExternalUserRequest $request, string $external_id): array { - return $this->fractal->item($request->getUserModel()) - ->transformWith($this->getTransformer(UserTransformer::class)) + $user = User::query()->where('external_id', $external_id)->firstOrFail(); + + return $this->fractal->item($user) + ->transformWith(UserTransformer::class) ->toArray(); } } diff --git a/app/Http/Controllers/Api/Application/Users/UserController.php b/app/Http/Controllers/Api/Application/Users/UserController.php index d845c9441..48fd58133 100644 --- a/app/Http/Controllers/Api/Application/Users/UserController.php +++ b/app/Http/Controllers/Api/Application/Users/UserController.php @@ -2,14 +2,19 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Users; +use Illuminate\Support\Arr; use Pterodactyl\Models\User; use Illuminate\Http\Response; use Illuminate\Http\JsonResponse; +use Spatie\QueryBuilder\QueryBuilder; +use Spatie\QueryBuilder\AllowedFilter; +use Illuminate\Database\Eloquent\Builder; use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserCreationService; use Pterodactyl\Services\Users\UserDeletionService; -use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Transformers\Api\Application\UserTransformer; +use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException; +use Pterodactyl\Http\Requests\Api\Application\Users\GetUserRequest; use Pterodactyl\Http\Requests\Api\Application\Users\GetUsersRequest; use Pterodactyl\Http\Requests\Api\Application\Users\StoreUserRequest; use Pterodactyl\Http\Requests\Api\Application\Users\DeleteUserRequest; @@ -18,62 +23,53 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; class UserController extends ApplicationApiController { - /** - * @var \Pterodactyl\Services\Users\UserCreationService - */ - private $creationService; - - /** - * @var \Pterodactyl\Services\Users\UserDeletionService - */ - private $deletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Services\Users\UserUpdateService - */ - private $updateService; - /** * UserController constructor. - * - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository - * @param \Pterodactyl\Services\Users\UserCreationService $creationService - * @param \Pterodactyl\Services\Users\UserDeletionService $deletionService - * @param \Pterodactyl\Services\Users\UserUpdateService $updateService */ public function __construct( - UserRepositoryInterface $repository, - UserCreationService $creationService, - UserDeletionService $deletionService, - UserUpdateService $updateService + private UserCreationService $creationService, + private UserDeletionService $deletionService, + private UserUpdateService $updateService ) { parent::__construct(); - - $this->creationService = $creationService; - $this->deletionService = $deletionService; - $this->repository = $repository; - $this->updateService = $updateService; } /** * Handle request to list all users on the panel. Returns a JSON-API representation * of a collection of users including any defined relations passed in * the request. - * - * @param \Pterodactyl\Http\Requests\Api\Application\Users\GetUsersRequest $request - * @return array */ public function index(GetUsersRequest $request): array { - $users = $this->repository->setSearchTerm($request->input('search'))->paginated(50); + $perPage = (int) $request->query('per_page', '10'); + if ($perPage < 1 || $perPage > 100) { + throw new QueryValueOutOfRangeHttpException('per_page', 1, 100); + } + + $users = QueryBuilder::for(User::query()) + ->allowedFilters([ + AllowedFilter::exact('id'), + AllowedFilter::exact('uuid'), + AllowedFilter::exact('external_id'), + 'username', + 'email', + AllowedFilter::callback('*', function (Builder $builder, $value) { + foreach (Arr::wrap($value) as $datum) { + $datum = '%' . $datum . '%'; + $builder->where(function (Builder $builder) use ($datum) { + $builder->where('uuid', 'LIKE', $datum) + ->orWhere('username', 'LIKE', $datum) + ->orWhere('email', 'LIKE', $datum) + ->orWhere('external_id', 'LIKE', $datum); + }); + } + }), + ]) + ->allowedSorts(['id', 'uuid', 'username', 'email', 'admin_role_id']) + ->paginate($perPage); return $this->fractal->collection($users) - ->transformWith($this->getTransformer(UserTransformer::class)) + ->transformWith(UserTransformer::class) ->toArray(); } @@ -81,13 +77,12 @@ class UserController extends ApplicationApiController * Handle a request to view a single user. Includes any relations that * were defined in the request. * - * @param \Pterodactyl\Http\Requests\Api\Application\Users\GetUsersRequest $request - * @return array + * @throws \Illuminate\Contracts\Container\BindingResolutionException */ - public function view(GetUsersRequest $request): array + public function view(GetUserRequest $request, User $user): array { - return $this->fractal->item($request->getModel(User::class)) - ->transformWith($this->getTransformer(UserTransformer::class)) + return $this->fractal->item($user) + ->transformWith(UserTransformer::class) ->toArray(); } @@ -99,50 +94,22 @@ class UserController extends ApplicationApiController * Revocation errors are returned under the 'revocation_errors' key in the response * meta. If there are no errors this is an empty array. * - * @param \Pterodactyl\Http\Requests\Api\Application\Users\UpdateUserRequest $request - * @return array - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Illuminate\Contracts\Container\BindingResolutionException */ - public function update(UpdateUserRequest $request): array + public function update(UpdateUserRequest $request, User $user): array { $this->updateService->setUserLevel(User::USER_LEVEL_ADMIN); - $collection = $this->updateService->handle($request->getModel(User::class), $request->validated()); + $user = $this->updateService->handle($user, $request->validated()); - $errors = []; - if (! empty($collection->get('exceptions'))) { - foreach ($collection->get('exceptions') as $node => $exception) { - /** @var \GuzzleHttp\Exception\RequestException $exception */ - /** @var \GuzzleHttp\Psr7\Response|null $response */ - $response = method_exists($exception, 'getResponse') ? $exception->getResponse() : null; - $message = trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ]); - - $errors[] = ['message' => $message, 'node' => $node]; - } - } - - $response = $this->fractal->item($collection->get('model')) - ->transformWith($this->getTransformer(UserTransformer::class)); - - if (count($errors) > 0) { - $response->addMeta([ - 'revocation_errors' => $errors, - ]); - } - - return $response->toArray(); + return $this->fractal->item($user) + ->transformWith(UserTransformer::class) + ->toArray(); } /** * Store a new user on the system. Returns the created user and a HTTP/201 * header on successful creation. * - * @param \Pterodactyl\Http\Requests\Api\Application\Users\StoreUserRequest $request - * @return \Illuminate\Http\JsonResponse - * * @throws \Exception * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ @@ -151,12 +118,7 @@ class UserController extends ApplicationApiController $user = $this->creationService->handle($request->validated()); return $this->fractal->item($user) - ->transformWith($this->getTransformer(UserTransformer::class)) - ->addMeta([ - 'resource' => route('api.application.users.view', [ - 'user' => $user->id, - ]), - ]) + ->transformWith(UserTransformer::class) ->respond(201); } @@ -164,15 +126,12 @@ class UserController extends ApplicationApiController * Handle a request to delete a user from the Panel. Returns a HTTP/204 response * on successful deletion. * - * @param \Pterodactyl\Http\Requests\Api\Application\Users\DeleteUserRequest $request - * @return \Illuminate\Http\Response - * * @throws \Pterodactyl\Exceptions\DisplayException */ - public function delete(DeleteUserRequest $request): Response + public function delete(DeleteUserRequest $request, User $user): Response { - $this->deletionService->handle($request->getModel(User::class)); + $this->deletionService->handle($user); - return response('', 204); + return $this->returnNoContent(); } } diff --git a/app/Http/Controllers/Api/Application/VersionController.php b/app/Http/Controllers/Api/Application/VersionController.php new file mode 100644 index 000000000..9bd2203ed --- /dev/null +++ b/app/Http/Controllers/Api/Application/VersionController.php @@ -0,0 +1,25 @@ +softwareVersionService->getVersionData()); + } +} diff --git a/app/Http/Controllers/Api/Client/AccountController.php b/app/Http/Controllers/Api/Client/AccountController.php new file mode 100644 index 000000000..468a751c0 --- /dev/null +++ b/app/Http/Controllers/Api/Client/AccountController.php @@ -0,0 +1,75 @@ +fractal->item($request->user()) + ->transformWith(AccountTransformer::class) + ->toArray(); + } + + /** + * Update the authenticated user's email address. + */ + public function updateEmail(UpdateEmailRequest $request): JsonResponse + { + $original = $request->user()->email; + $this->updateService->handle($request->user(), $request->validated()); + + if ($original !== $request->input('email')) { + Activity::event('user:account.email-changed') + ->property(['old' => $original, 'new' => $request->input('email')]) + ->log(); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Update the authenticated user's password. All existing sessions will be logged + * out immediately. + * + * @throws \Throwable + */ + public function updatePassword(UpdatePasswordRequest $request): JsonResponse + { + $user = $this->updateService->handle($request->user(), $request->validated()); + + $guard = $this->manager->guard(); + // If you do not update the user in the session you'll end up working with a + // cached copy of the user that does not include the updated password. Do this + // to correctly store the new user details in the guard and allow the logout + // other devices functionality to work. + $guard->setUser($user); + + // This method doesn't exist in the stateless Sanctum world. + if (method_exists($guard, 'logoutOtherDevices')) { + $guard->logoutOtherDevices($request->input('password')); + } + + Activity::event('user:account.password-changed')->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/ActivityLogController.php b/app/Http/Controllers/Api/Client/ActivityLogController.php new file mode 100644 index 000000000..f6e9033e2 --- /dev/null +++ b/app/Http/Controllers/Api/Client/ActivityLogController.php @@ -0,0 +1,30 @@ +user()->activity()) + ->with('actor') + ->allowedFilters([AllowedFilter::partial('event')]) + ->allowedSorts(['timestamp']) + ->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS) + ->paginate(min($request->query('per_page', 25), 100)) + ->appends($request->query()); + + return $this->fractal->collection($activity) + ->transformWith(ActivityLogTransformer::class) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/ApiKeyController.php b/app/Http/Controllers/Api/Client/ApiKeyController.php new file mode 100644 index 000000000..e60ac59cd --- /dev/null +++ b/app/Http/Controllers/Api/Client/ApiKeyController.php @@ -0,0 +1,71 @@ +fractal->collection($request->user()->apiKeys) + ->transformWith(ApiKeyTransformer::class) + ->toArray(); + } + + /** + * Store a new API key for a user's account. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function store(StoreApiKeyRequest $request): array + { + if ($request->user()->apiKeys->count() >= 25) { + throw new DisplayException('You have reached the account limit for number of API keys.'); + } + + $token = $request->user()->createToken( + $request->input('description'), + $request->input('allowed_ips') + ); + + Activity::event('user:api-key.create') + ->subject($token->accessToken) + ->property('identifier', $token->accessToken->identifier) + ->log(); + + return $this->fractal->item($token->accessToken) + ->transformWith(ApiKeyTransformer::class) + ->addMeta(['secret_token' => $token->plainTextToken]) + ->toArray(); + } + + /** + * Deletes a given API key. + */ + public function delete(ClientApiRequest $request, string $identifier): JsonResponse + { + /** @var \Pterodactyl\Models\ApiKey $key */ + $key = $request->user()->apiKeys() + ->where('key_type', ApiKey::TYPE_ACCOUNT) + ->where('identifier', $identifier) + ->firstOrFail(); + + Activity::event('user:api-key.delete') + ->property('identifier', $key->identifier) + ->log(); + + $key->delete(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/ClientApiController.php b/app/Http/Controllers/Api/Client/ClientApiController.php index e2d4b3f83..12a09cb04 100644 --- a/app/Http/Controllers/Api/Client/ClientApiController.php +++ b/app/Http/Controllers/Api/Client/ClientApiController.php @@ -2,28 +2,36 @@ namespace Pterodactyl\Http\Controllers\Api\Client; -use Webmozart\Assert\Assert; -use Illuminate\Container\Container; -use Pterodactyl\Transformers\Api\Client\BaseClientTransformer; +use Pterodactyl\Transformers\Api\Transformer; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; abstract class ClientApiController extends ApplicationApiController { /** - * Return an instance of an application transformer. - * - * @param string $abstract - * @return \Pterodactyl\Transformers\Api\Client\BaseClientTransformer + * Returns only the includes which are valid for the given transformer. */ - public function getTransformer(string $abstract) + protected function getIncludesForTransformer(Transformer $transformer, array $merge = []): array { - /** @var \Pterodactyl\Transformers\Api\Client\BaseClientTransformer $transformer */ - $transformer = Container::getInstance()->make($abstract); - Assert::isInstanceOf($transformer, BaseClientTransformer::class); + $filtered = array_filter($this->parseIncludes(), function ($datum) use ($transformer) { + return in_array($datum, $transformer->getAvailableIncludes()); + }); - $transformer->setKey($this->request->attributes->get('api_key')); - $transformer->setUser($this->request->user()); + return array_merge($filtered, $merge); + } - return $transformer; + /** + * Returns the parsed includes for this request. + */ + protected function parseIncludes(): array + { + $includes = $this->request->query('include') ?? []; + + if (!is_string($includes)) { + return $includes; + } + + return array_map(function ($item) { + return trim($item); + }, explode(',', $includes)); } } diff --git a/app/Http/Controllers/Api/Client/ClientController.php b/app/Http/Controllers/Api/Client/ClientController.php index 7b8f4b23d..d4d5ae6c5 100644 --- a/app/Http/Controllers/Api/Client/ClientController.php +++ b/app/Http/Controllers/Api/Client/ClientController.php @@ -2,62 +2,85 @@ namespace Pterodactyl\Http\Controllers\Api\Client; -use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; +use Pterodactyl\Models\Permission; +use Spatie\QueryBuilder\QueryBuilder; +use Spatie\QueryBuilder\AllowedFilter; +use Pterodactyl\Models\Filters\MultiFieldServerFilter; use Pterodactyl\Transformers\Api\Client\ServerTransformer; use Pterodactyl\Http\Requests\Api\Client\GetServersRequest; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class ClientController extends ClientApiController { - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - /** * ClientController constructor. - * - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ - public function __construct(ServerRepositoryInterface $repository) + public function __construct() { parent::__construct(); - - $this->repository = $repository; } /** - * Return all of the servers available to the client making the API + * Return all the servers available to the client making the API * request, including servers the user has access to as a subuser. - * - * @param \Pterodactyl\Http\Requests\Api\Client\GetServersRequest $request - * @return array */ public function index(GetServersRequest $request): array { - // Check for the filter parameter on the request. - switch ($request->input('filter')) { - case 'all': - $filter = User::FILTER_LEVEL_ALL; - break; - case 'admin': - $filter = User::FILTER_LEVEL_ADMIN; - break; - case 'owner': - $filter = User::FILTER_LEVEL_OWNER; - break; - case 'subuser-of': - default: - $filter = User::FILTER_LEVEL_SUBUSER; - break; + $user = $request->user(); + $transformer = new ServerTransformer(); + + // Start the query builder and ensure we eager load any requested relationships from the request. + $builder = QueryBuilder::for( + Server::query()->with($this->getIncludesForTransformer($transformer, ['node'])) + )->allowedFilters([ + 'uuid', + 'name', + 'description', + 'external_id', + AllowedFilter::custom('*', new MultiFieldServerFilter()), + ]); + + $loweredBindings = collect($builder->getBindings()) + ->map(fn ($f, $key) => is_string($f) ? strtolower($f) : $f) + ->all(); + $builder->setBindings($loweredBindings); + + $type = $request->input('type'); + // Either return all the servers the user has access to because they are an admin `?type=admin` or + // just return all the servers the user has access to because they are the owner or a subuser of the + // server. If ?type=admin-all is passed all servers on the system will be returned to the user, rather + // than only servers they can see because they are an admin. + if (in_array($type, ['admin', 'admin-all'])) { + // If they aren't an admin but want all the admin servers don't fail the request, just + // make it a query that will never return any results back. + if (!$user->root_admin) { + $builder->whereRaw('1 = 2'); + } else { + $builder = $type === 'admin-all' + ? $builder + : $builder->whereNotIn('servers.id', $user->accessibleServers()->pluck('id')->all()); + } + } elseif ($type === 'owner') { + $builder = $builder->where('servers.owner_id', $user->id); + } else { + $builder = $builder->whereIn('servers.id', $user->accessibleServers()->pluck('id')->all()); } - $servers = $this->repository->filterUserAccessServers( - $request->user(), $filter, config('pterodactyl.paginate.frontend.servers') - ); + $servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query()); - return $this->fractal->collection($servers) - ->transformWith($this->getTransformer(ServerTransformer::class)) - ->toArray(); + return $this->fractal->transformWith($transformer)->collection($servers)->toArray(); + } + + /** + * Returns all the subuser permissions available on the system. + */ + public function permissions(): array + { + return [ + 'object' => 'system_permissions', + 'attributes' => [ + 'permissions' => Permission::permissions(), + ], + ]; } } diff --git a/app/Http/Controllers/Api/Client/SSHKeyController.php b/app/Http/Controllers/Api/Client/SSHKeyController.php new file mode 100644 index 000000000..794c44cf0 --- /dev/null +++ b/app/Http/Controllers/Api/Client/SSHKeyController.php @@ -0,0 +1,67 @@ +fractal->collection($request->user()->sshKeys) + ->transformWith(UserSSHKeyTransformer::class) + ->toArray(); + } + + /** + * Stores a new SSH key for the authenticated user's account. + */ + public function store(StoreSSHKeyRequest $request): array + { + $model = $request->user()->sshKeys()->create([ + 'name' => $request->input('name'), + 'public_key' => $request->getPublicKey(), + 'fingerprint' => $request->getKeyFingerprint(), + ]); + + Activity::event('user:ssh-key.create') + ->subject($model) + ->property('fingerprint', $request->getKeyFingerprint()) + ->log(); + + return $this->fractal->item($model) + ->transformWith(UserSSHKeyTransformer::class) + ->toArray(); + } + + /** + * Deletes an SSH key from the user's account. + */ + public function delete(ClientApiRequest $request): JsonResponse + { + $this->validate($request, ['fingerprint' => ['required', 'string']]); + + $key = $request->user()->sshKeys() + ->where('fingerprint', $request->input('fingerprint')) + ->first(); + + if (!is_null($key)) { + $key->delete(); + + Activity::event('user:ssh-key.delete') + ->subject($key) + ->property('fingerprint', $key->fingerprint) + ->log(); + } + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ActivityLogController.php b/app/Http/Controllers/Api/Client/Servers/ActivityLogController.php new file mode 100644 index 000000000..e3e083d48 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ActivityLogController.php @@ -0,0 +1,54 @@ +authorize(Permission::ACTION_ACTIVITY_READ, $server); + + $activity = QueryBuilder::for($server->activity()) + ->with('actor') + ->allowedSorts(['timestamp']) + ->allowedFilters([AllowedFilter::partial('event')]) + ->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS) + ->when(config('activity.hide_admin_activity'), function (Builder $builder) use ($server) { + // We could do this with a query and a lot of joins, but that gets pretty + // painful so for now we'll execute a simpler query. + $subusers = $server->subusers()->pluck('user_id')->merge($server->owner_id); + + $builder->select('activity_logs.*') + ->leftJoin('users', function (JoinClause $join) { + $join->on('users.id', 'activity_logs.actor_id') + ->where('activity_logs.actor_type', (new User())->getMorphClass()); + }) + ->where(function (Builder $builder) use ($subusers) { + $builder->whereNull('users.id') + ->orWhere('users.root_admin', 0) + ->orWhereIn('users.id', $subusers); + }); + }) + ->paginate(min($request->query('per_page', 25), 100)) + ->appends($request->query()); + + return $this->fractal->collection($activity) + ->transformWith(ActivityLogTransformer::class) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/BackupController.php b/app/Http/Controllers/Api/Client/Servers/BackupController.php new file mode 100644 index 000000000..8ee5f7b0e --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/BackupController.php @@ -0,0 +1,224 @@ +user()->can(Permission::ACTION_BACKUP_READ, $server)) { + throw new AuthorizationException(); + } + + $limit = min($request->query('per_page') ?? 20, 50); + + return $this->fractal->collection($server->backups()->paginate($limit)) + ->transformWith(BackupTransformer::class) + ->addMeta([ + 'backup_count' => $this->repository->getNonFailedBackups($server)->count(), + ]) + ->toArray(); + } + + /** + * Starts the backup process for a server. + * + * @throws \Spatie\Fractalistic\Exceptions\InvalidTransformation + * @throws \Spatie\Fractalistic\Exceptions\NoTransformerSpecified + * @throws \Throwable + */ + public function store(StoreBackupRequest $request, Server $server): array + { + $action = $this->initiateBackupService + ->setIgnoredFiles(explode(PHP_EOL, $request->input('ignored') ?? '')); + + // Only set the lock status if the user even has permission to delete backups, + // otherwise ignore this status. This gets a little funky since it isn't clear + // how best to allow a user to create a backup that is locked without also preventing + // them from just filling up a server with backups that can never be deleted? + if ($request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + $action->setIsLocked((bool) $request->input('is_locked')); + } + + $backup = $action->handle($server, $request->input('name')); + + Activity::event('server:backup.start') + ->subject($backup) + ->property(['name' => $backup->name, 'locked' => (bool) $request->input('is_locked')]) + ->log(); + + return $this->fractal->item($backup) + ->transformWith(BackupTransformer::class) + ->toArray(); + } + + /** + * Toggles the lock status of a given backup for a server. + * + * @throws \Throwable + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function toggleLock(Request $request, Server $server, Backup $backup): array + { + if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + throw new AuthorizationException(); + } + + $action = $backup->is_locked ? 'server:backup.unlock' : 'server:backup.lock'; + + $backup->update(['is_locked' => !$backup->is_locked]); + + Activity::event($action)->subject($backup)->property('name', $backup->name)->log(); + + return $this->fractal->item($backup) + ->transformWith(BackupTransformer::class) + ->toArray(); + } + + /** + * Returns information about a single backup. + * + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function view(Request $request, Server $server, Backup $backup): array + { + if (!$request->user()->can(Permission::ACTION_BACKUP_READ, $server)) { + throw new AuthorizationException(); + } + + return $this->fractal->item($backup) + ->transformWith(BackupTransformer::class) + ->toArray(); + } + + /** + * Deletes a backup from the panel as well as the remote source where it is currently + * being stored. + * + * @throws \Throwable + */ + public function delete(Request $request, Server $server, Backup $backup): JsonResponse + { + if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + throw new AuthorizationException(); + } + + $this->deleteBackupService->handle($backup); + + Activity::event('server:backup.delete') + ->subject($backup) + ->property(['name' => $backup->name, 'failed' => !$backup->is_successful]) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Download the backup for a given server instance. For daemon local files, the file + * will be streamed back through the Panel. For AWS S3 files, a signed URL will be generated + * which the user is redirected to. + * + * @throws \Throwable + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function download(Request $request, Server $server, Backup $backup): JsonResponse + { + if (!$request->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) { + throw new AuthorizationException(); + } + + if ($backup->disk !== Backup::ADAPTER_AWS_S3 && $backup->disk !== Backup::ADAPTER_WINGS) { + throw new BadRequestHttpException('The backup requested references an unknown disk driver type and cannot be downloaded.'); + } + + $url = $this->downloadLinkService->handle($backup, $request->user()); + + Activity::event('server:backup.download')->subject($backup)->property('name', $backup->name)->log(); + + return new JsonResponse([ + 'object' => 'signed_url', + 'attributes' => ['url' => $url], + ]); + } + + /** + * Handles restoring a backup by making a request to the Wings instance telling it + * to begin the process of finding (or downloading) the backup and unpacking it + * over the server files. + * + * If the "truncate" flag is passed through in this request then all the + * files that currently exist on the server will be deleted before restoring. + * Otherwise, the archive will simply be unpacked over the existing files. + * + * @throws \Throwable + */ + public function restore(RestoreBackupRequest $request, Server $server, Backup $backup): JsonResponse + { + // Cannot restore a backup unless a server is fully installed and not currently + // processing a different backup restoration request. + if (!is_null($server->status)) { + throw new BadRequestHttpException('This server is not currently in a state that allows for a backup to be restored.'); + } + + if (!$backup->is_successful && is_null($backup->completed_at)) { + throw new BadRequestHttpException('This backup cannot be restored at this time: not completed or failed.'); + } + + $log = Activity::event('server:backup.restore') + ->subject($backup) + ->property(['name' => $backup->name, 'truncate' => $request->input('truncate')]); + + $log->transaction(function () use ($backup, $server, $request) { + // If the backup is for an S3 file we need to generate a unique Download link for + // it that will allow Wings to actually access the file. + if ($backup->disk === Backup::ADAPTER_AWS_S3) { + $url = $this->downloadLinkService->handle($backup, $request->user()); + } + + // Update the status right away for the server so that we know not to allow certain + // actions against it via the Panel API. + $server->update(['status' => Server::STATUS_RESTORING_BACKUP]); + + $this->daemonRepository->setServer($server)->restore($backup, $url ?? null, $request->input('truncate')); + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/CommandController.php b/app/Http/Controllers/Api/Client/Servers/CommandController.php index 881548fa0..6cc50de47 100644 --- a/app/Http/Controllers/Api/Client/Servers/CommandController.php +++ b/app/Http/Controllers/Api/Client/Servers/CommandController.php @@ -4,61 +4,51 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Illuminate\Http\Response; use Pterodactyl\Models\Server; +use Pterodactyl\Facades\Activity; use Psr\Http\Message\ResponseInterface; -use GuzzleHttp\Exception\ClientException; -use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\BadResponseException; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Pterodactyl\Repositories\Wings\DaemonCommandRepository; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\SendCommandRequest; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; -use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; class CommandController extends ClientApiController { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface - */ - private $repository; - /** * CommandController constructor. - * - * @param \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface $repository */ - public function __construct(CommandRepositoryInterface $repository) + public function __construct(private DaemonCommandRepository $repository) { parent::__construct(); - - $this->repository = $repository; } /** * Send a command to a running server. * - * @param \Pterodactyl\Http\Requests\Api\Client\Servers\SendCommandRequest $request - * @return \Illuminate\Http\Response - * * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ - public function index(SendCommandRequest $request): Response + public function index(SendCommandRequest $request, Server $server): Response { - $server = $request->getModel(Server::class); - $token = $request->attributes->get('server_token'); - try { - $this->repository->setServer($server) - ->setToken($token) - ->send($request->input('command')); - } catch (RequestException $exception) { - if ($exception instanceof ClientException) { - if ($exception->getResponse() instanceof ResponseInterface && $exception->getResponse()->getStatusCode() === 412) { - throw new PreconditionFailedHttpException('Server is not online.'); + $this->repository->setServer($server)->send($request->input('command')); + } catch (DaemonConnectionException $exception) { + $previous = $exception->getPrevious(); + + if ($previous instanceof BadResponseException) { + if ( + $previous->getResponse() instanceof ResponseInterface + && $previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY + ) { + throw new HttpException(Response::HTTP_BAD_GATEWAY, 'Server must be online in order to send commands.', $exception); } } - throw new DaemonConnectionException($exception); + throw $exception; } + Activity::event('server:console.command')->property('command', $request->input('command'))->log(); + return $this->returnNoContent(); } } diff --git a/app/Http/Controllers/Api/Client/Servers/DatabaseController.php b/app/Http/Controllers/Api/Client/Servers/DatabaseController.php new file mode 100644 index 000000000..5173cab2c --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/DatabaseController.php @@ -0,0 +1,102 @@ +fractal->collection($server->databases) + ->transformWith(DatabaseTransformer::class) + ->toArray(); + } + + /** + * Create a new database for the given server and return it. + * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException + * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException + */ + public function store(StoreDatabaseRequest $request, Server $server): array + { + $database = $this->deployDatabaseService->handle($server, $request->validated()); + + Activity::event('server:database.create') + ->subject($database) + ->property('name', $database->database) + ->log(); + + return $this->fractal->item($database) + ->parseIncludes(['password']) + ->transformWith(DatabaseTransformer::class) + ->toArray(); + } + + /** + * Rotates the password for the given server model and returns a fresh instance to + * the caller. + * + * @throws \Throwable + */ + public function rotatePassword(RotatePasswordRequest $request, Server $server, Database $database): array + { + $this->passwordService->handle($database); + $database->refresh(); + + Activity::event('server:database.rotate-password') + ->subject($database) + ->property('name', $database->database) + ->log(); + + return $this->fractal->item($database) + ->parseIncludes(['password']) + ->transformWith(DatabaseTransformer::class) + ->toArray(); + } + + /** + * Removes a database from the server. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function delete(DeleteDatabaseRequest $request, Server $server, Database $database): Response + { + $this->managementService->delete($database); + + Activity::event('server:database.delete') + ->subject($database) + ->property('name', $database->database) + ->log(); + + return new Response('', Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php new file mode 100644 index 000000000..eaee7f8cc --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -0,0 +1,265 @@ +fileRepository + ->setServer($server) + ->getDirectory($request->get('directory') ?? '/'); + + return $this->fractal->collection($contents) + ->transformWith(FileObjectTransformer::class) + ->toArray(); + } + + /** + * Return the contents of a specified file for the user. + * + * @throws \Throwable + */ + public function contents(GetFileContentsRequest $request, Server $server): Response + { + $response = $this->fileRepository->setServer($server)->getContent( + $request->get('file'), + config('pterodactyl.files.max_edit_size') + ); + + Activity::event('server:file.read')->property('file', $request->get('file'))->log(); + + return new Response($response, Response::HTTP_OK, ['Content-Type' => 'text/plain']); + } + + /** + * Generates a one-time token with a link that the user can use to + * download a given file. + * + * @throws \Throwable + */ + public function download(GetFileContentsRequest $request, Server $server): array + { + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setUser($request->user()) + ->setClaims([ + 'file_path' => rawurldecode($request->get('file')), + 'server_uuid' => $server->uuid, + ]) + ->handle($server->node, $request->user()->id . $server->uuid); + + Activity::event('server:file.download')->property('file', $request->get('file'))->log(); + + return [ + 'object' => 'signed_url', + 'attributes' => [ + 'url' => sprintf( + '%s/download/file?token=%s', + $server->node->getConnectionAddress(), + $token->toString() + ), + ], + ]; + } + + /** + * Writes the contents of the specified file to the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function write(WriteFileContentRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->putContent($request->get('file'), $request->getContent()); + + Activity::event('server:file.write')->property('file', $request->get('file'))->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Creates a new folder on the server. + * + * @throws \Throwable + */ + public function create(CreateFolderRequest $request, Server $server): JsonResponse + { + $this->fileRepository + ->setServer($server) + ->createDirectory($request->input('name'), $request->input('root', '/')); + + Activity::event('server:file.create-directory') + ->property('name', $request->input('name')) + ->property('directory', $request->input('root')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Renames a file on the remote machine. + * + * @throws \Throwable + */ + public function rename(RenameFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository + ->setServer($server) + ->renameFiles($request->input('root'), $request->input('files')); + + Activity::event('server:file.rename') + ->property('directory', $request->input('root')) + ->property('files', $request->input('files')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Copies a file on the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function copy(CopyFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository + ->setServer($server) + ->copyFile($request->input('location')); + + Activity::event('server:file.copy')->property('file', $request->input('location'))->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function compress(CompressFilesRequest $request, Server $server): array + { + $file = $this->fileRepository->setServer($server)->compressFiles( + $request->input('root'), + $request->input('files') + ); + + Activity::event('server:file.compress') + ->property('directory', $request->input('root')) + ->property('files', $request->input('files')) + ->log(); + + return $this->fractal->item($file) + ->transformWith(FileObjectTransformer::class) + ->toArray(); + } + + /** + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse + { + set_time_limit(300); + + $this->fileRepository->setServer($server)->decompressFile( + $request->input('root'), + $request->input('file') + ); + + Activity::event('server:file.decompress') + ->property('directory', $request->input('root')) + ->property('files', $request->input('file')) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Deletes files or folders for the server in the given root directory. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function delete(DeleteFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->deleteFiles( + $request->input('root'), + $request->input('files') + ); + + Activity::event('server:file.delete') + ->property('directory', $request->input('root')) + ->property('files', $request->input('files')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Updates file permissions for file(s) in the given root directory. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function chmod(ChmodFilesRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->chmodFiles( + $request->input('root'), + $request->input('files') + ); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Requests that a file be downloaded from a remote location by Wings. + * + * @throws \Throwable + */ + public function pull(PullFileRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server)->pull( + $request->input('url'), + $request->input('directory'), + $request->safe(['filename', 'use_header', 'foreground']) + ); + + Activity::event('server:file.pull') + ->property('directory', $request->input('directory')) + ->property('url', $request->input('url')) + ->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/FileUploadController.php b/app/Http/Controllers/Api/Client/Servers/FileUploadController.php new file mode 100644 index 000000000..98fcd587d --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/FileUploadController.php @@ -0,0 +1,54 @@ + 'signed_url', + 'attributes' => [ + 'url' => $this->getUploadUrl($server, $request->user()), + ], + ]); + } + + /** + * Returns an url where files can be uploaded to. + */ + protected function getUploadUrl(Server $server, User $user): string + { + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setUser($user) + ->setClaims(['server_uuid' => $server->uuid]) + ->handle($server->node, $user->id . $server->uuid); + + return sprintf( + '%s/upload/file?token=%s', + $server->node->getConnectionAddress(), + $token->toString() + ); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php b/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php new file mode 100644 index 000000000..2fd97101b --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php @@ -0,0 +1,140 @@ +fractal->collection($server->allocations) + ->transformWith(AllocationTransformer::class) + ->toArray(); + } + + /** + * Set the primary allocation for a server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateAllocationRequest $request, Server $server, Allocation $allocation): array + { + $original = $allocation->notes; + + $allocation->forceFill(['notes' => $request->input('notes')])->save(); + + if ($original !== $allocation->notes) { + Activity::event('server:allocation.notes') + ->subject($allocation) + ->property(['allocation' => $allocation->toString(), 'old' => $original, 'new' => $allocation->notes]) + ->log(); + } + + return $this->fractal->item($allocation) + ->transformWith(AllocationTransformer::class) + ->toArray(); + } + + /** + * Set the primary allocation for a server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function setPrimary(SetPrimaryAllocationRequest $request, Server $server, Allocation $allocation): array + { + $this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]); + + Activity::event('server:allocation.primary') + ->subject($allocation) + ->property('allocation', $allocation->toString()) + ->log(); + + return $this->fractal->item($allocation) + ->transformWith(AllocationTransformer::class) + ->toArray(); + } + + /** + * Set the notes for the allocation for a server. + *s. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function store(NewAllocationRequest $request, Server $server): array + { + if ($server->allocations()->count() >= $server->allocation_limit) { + throw new DisplayException('Cannot assign additional allocations to this server: limit has been reached.'); + } + + $allocation = $this->assignableAllocationService->handle($server); + + Activity::event('server:allocation.create') + ->subject($allocation) + ->property('allocation', $allocation->toString()) + ->log(); + + return $this->fractal->item($allocation) + ->transformWith(AllocationTransformer::class) + ->toArray(); + } + + /** + * Delete an allocation from a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(DeleteAllocationRequest $request, Server $server, Allocation $allocation): JsonResponse + { + // Don't allow the deletion of allocations if the server does not have an + // allocation limit set. + if (empty($server->allocation_limit)) { + throw new DisplayException('You cannot delete allocations for this server: no allocation limit is set.'); + } + + if ($allocation->id === $server->allocation_id) { + throw new DisplayException('You cannot delete the primary allocation for this server.'); + } + + Allocation::query()->where('id', $allocation->id)->update([ + 'notes' => null, + 'server_id' => null, + ]); + + Activity::event('server:allocation.delete') + ->subject($allocation) + ->property('allocation', $allocation->toString()) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/PowerController.php b/app/Http/Controllers/Api/Client/Servers/PowerController.php index 7a96a2a4e..ca0575765 100644 --- a/app/Http/Controllers/Api/Client/Servers/PowerController.php +++ b/app/Http/Controllers/Api/Client/Servers/PowerController.php @@ -4,43 +4,31 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Illuminate\Http\Response; use Pterodactyl\Models\Server; +use Pterodactyl\Facades\Activity; +use Pterodactyl\Repositories\Wings\DaemonPowerRepository; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\SendPowerRequest; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; class PowerController extends ClientApiController { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface - */ - private $repository; - /** * PowerController constructor. - * - * @param \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface $repository */ - public function __construct(PowerRepositoryInterface $repository) + public function __construct(private DaemonPowerRepository $repository) { parent::__construct(); - - $this->repository = $repository; } /** * Send a power action to a server. - * - * @param \Pterodactyl\Http\Requests\Api\Client\Servers\SendPowerRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException */ - public function index(SendPowerRequest $request): Response + public function index(SendPowerRequest $request, Server $server): Response { - $server = $request->getModel(Server::class); - $token = $request->attributes->get('server_token'); + $this->repository->setServer($server)->send( + $request->input('signal') + ); - $this->repository->setServer($server)->setToken($token)->sendSignal($request->input('signal')); + Activity::event(strtolower("server:power.{$request->input('signal')}"))->log(); return $this->returnNoContent(); } diff --git a/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php b/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php index 75645e9af..daf3219a1 100644 --- a/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php +++ b/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php @@ -2,23 +2,40 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; +use Carbon\Carbon; use Pterodactyl\Models\Server; +use Illuminate\Cache\Repository; use Pterodactyl\Transformers\Api\Client\StatsTransformer; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest; class ResourceUtilizationController extends ClientApiController { /** - * Return the current resource utilization for a server. - * - * @param \Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest $request - * @return array + * ResourceUtilizationController constructor. */ - public function index(GetServerRequest $request): array + public function __construct(private Repository $cache, private DaemonServerRepository $repository) { - return $this->fractal->item($request->getModel(Server::class)) - ->transformWith($this->getTransformer(StatsTransformer::class)) + parent::__construct(); + } + + /** + * Return the current resource utilization for a server. This value is cached for up to + * 20 seconds at a time to ensure that repeated requests to this endpoint do not cause + * a flood of unnecessary API calls. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function __invoke(GetServerRequest $request, Server $server): array + { + $key = "resources:$server->uuid"; + $stats = $this->cache->remember($key, Carbon::now()->addSeconds(20), function () use ($server) { + return $this->repository->setServer($server)->getDetails(); + }); + + return $this->fractal->item($stats) + ->transformWith(StatsTransformer::class) ->toArray(); } } diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleController.php new file mode 100644 index 000000000..20fc4e658 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleController.php @@ -0,0 +1,184 @@ +schedules->loadMissing('tasks'); + + return $this->fractal->collection($schedules) + ->transformWith(ScheduleTransformer::class) + ->toArray(); + } + + /** + * Store a new schedule for a server. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(StoreScheduleRequest $request, Server $server): array + { + /** @var \Pterodactyl\Models\Schedule $model */ + $model = $this->repository->create([ + 'server_id' => $server->id, + 'name' => $request->input('name'), + 'cron_day_of_week' => $request->input('day_of_week'), + 'cron_month' => $request->input('month'), + 'cron_day_of_month' => $request->input('day_of_month'), + 'cron_hour' => $request->input('hour'), + 'cron_minute' => $request->input('minute'), + 'is_active' => (bool) $request->input('is_active'), + 'only_when_online' => (bool) $request->input('only_when_online'), + 'next_run_at' => $this->getNextRunAt($request), + ]); + + Activity::event('server:schedule.create') + ->subject($model) + ->property('name', $model->name) + ->log(); + + return $this->fractal->item($model) + ->transformWith(ScheduleTransformer::class) + ->toArray(); + } + + /** + * Returns a specific schedule for the server. + */ + public function view(ViewScheduleRequest $request, Server $server, Schedule $schedule): array + { + if ($schedule->server_id !== $server->id) { + throw new NotFoundHttpException(); + } + + $schedule->loadMissing('tasks'); + + return $this->fractal->item($schedule) + ->transformWith(ScheduleTransformer::class) + ->toArray(); + } + + /** + * Updates a given schedule with the new data provided. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateScheduleRequest $request, Server $server, Schedule $schedule): array + { + $active = (bool) $request->input('is_active'); + + $data = [ + 'name' => $request->input('name'), + 'cron_day_of_week' => $request->input('day_of_week'), + 'cron_month' => $request->input('month'), + 'cron_day_of_month' => $request->input('day_of_month'), + 'cron_hour' => $request->input('hour'), + 'cron_minute' => $request->input('minute'), + 'is_active' => $active, + 'only_when_online' => (bool) $request->input('only_when_online'), + 'next_run_at' => $this->getNextRunAt($request), + ]; + + // Toggle the processing state of the scheduled task when it is enabled or disabled so that an + // invalid state can be reset without manual database intervention. + // + // @see https://github.com/pterodactyl/panel/issues/2425 + if ($schedule->is_active !== $active) { + $data['is_processing'] = false; + } + + $this->repository->update($schedule->id, $data); + + Activity::event('server:schedule.update') + ->subject($schedule) + ->property(['name' => $schedule->name, 'active' => $active]) + ->log(); + + return $this->fractal->item($schedule->refresh()) + ->transformWith(ScheduleTransformer::class) + ->toArray(); + } + + /** + * Executes a given schedule immediately rather than waiting on it's normally scheduled time + * to pass. This does not care about the schedule state. + * + * @throws \Throwable + */ + public function execute(TriggerScheduleRequest $request, Server $server, Schedule $schedule): JsonResponse + { + $this->service->handle($schedule, true); + + Activity::event('server:schedule.execute')->subject($schedule)->property('name', $schedule->name)->log(); + + return new JsonResponse([], JsonResponse::HTTP_ACCEPTED); + } + + /** + * Deletes a schedule and it's associated tasks. + */ + public function delete(DeleteScheduleRequest $request, Server $server, Schedule $schedule): JsonResponse + { + $this->repository->delete($schedule->id); + + Activity::event('server:schedule.delete')->subject($schedule)->property('name', $schedule->name)->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Get the next run timestamp based on the cron data provided. + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + protected function getNextRunAt(Request $request): Carbon + { + try { + return Utilities::getScheduleNextRunDate( + $request->input('minute'), + $request->input('hour'), + $request->input('day_of_month'), + $request->input('month'), + $request->input('day_of_week') + ); + } catch (\Exception $exception) { + throw new DisplayException('The cron data provided does not evaluate to a valid expression.'); + } + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php new file mode 100644 index 000000000..47c1a0b47 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php @@ -0,0 +1,175 @@ +tasks()->count() >= $limit) { + throw new ServiceLimitExceededException("Schedules may not have more than $limit tasks associated with them. Creating this task would put this schedule over the limit."); + } + + if ($server->backup_limit === 0 && $request->action === 'backup') { + throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0."); + } + + /** @var \Pterodactyl\Models\Task|null $lastTask */ + $lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first(); + + /** @var \Pterodactyl\Models\Task $task */ + $task = $this->connection->transaction(function () use ($request, $schedule, $lastTask) { + $sequenceId = ($lastTask->sequence_id ?? 0) + 1; + $requestSequenceId = $request->integer('sequence_id', $sequenceId); + + // Ensure that the sequence id is at least 1. + if ($requestSequenceId < 1) { + $requestSequenceId = 1; + } + + // If the sequence id from the request is greater than or equal to the next available + // sequence id, we don't need to do anything special. Otherwise, we need to update + // the sequence id of all tasks that are greater than or equal to the request sequence + // id to be one greater than the current value. + if ($requestSequenceId < $sequenceId) { + $schedule->tasks() + ->where('sequence_id', '>=', $requestSequenceId) + ->increment('sequence_id'); + $sequenceId = $requestSequenceId; + } + + return $this->repository->create([ + 'schedule_id' => $schedule->id, + 'sequence_id' => $sequenceId, + 'action' => $request->input('action'), + 'payload' => $request->input('payload') ?? '', + 'time_offset' => $request->input('time_offset'), + 'continue_on_failure' => $request->boolean('continue_on_failure'), + ]); + }); + + Activity::event('server:task.create') + ->subject($schedule, $task) + ->property(['name' => $schedule->name, 'action' => $task->action, 'payload' => $task->payload]) + ->log(); + + return $this->fractal->item($task) + ->transformWith(TaskTransformer::class) + ->toArray(); + } + + /** + * Updates a given task for a server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(StoreTaskRequest $request, Server $server, Schedule $schedule, Task $task): array + { + if ($schedule->id !== $task->schedule_id || $server->id !== $schedule->server_id) { + throw new NotFoundHttpException(); + } + + if ($server->backup_limit === 0 && $request->action === 'backup') { + throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0."); + } + + $this->connection->transaction(function () use ($request, $schedule, $task) { + $sequenceId = $request->integer('sequence_id', $task->sequence_id); + // Ensure that the sequence id is at least 1. + if ($sequenceId < 1) { + $sequenceId = 1; + } + + // Shift all other tasks in the schedule up or down to make room for the new task. + if ($sequenceId < $task->sequence_id) { + $schedule->tasks() + ->where('sequence_id', '>=', $sequenceId) + ->where('sequence_id', '<', $task->sequence_id) + ->increment('sequence_id'); + } elseif ($sequenceId > $task->sequence_id) { + $schedule->tasks() + ->where('sequence_id', '>', $task->sequence_id) + ->where('sequence_id', '<=', $sequenceId) + ->decrement('sequence_id'); + } + + $this->repository->update($task->id, [ + 'sequence_id' => $sequenceId, + 'action' => $request->input('action'), + 'payload' => $request->input('payload') ?? '', + 'time_offset' => $request->input('time_offset'), + 'continue_on_failure' => $request->boolean('continue_on_failure'), + ]); + }); + + Activity::event('server:task.update') + ->subject($schedule, $task) + ->property(['name' => $schedule->name, 'action' => $task->action, 'payload' => $task->payload]) + ->log(); + + return $this->fractal->item($task->refresh()) + ->transformWith(TaskTransformer::class) + ->toArray(); + } + + /** + * Delete a given task for a schedule. If there are subsequent tasks stored in the database + * for this schedule their sequence IDs are decremented properly. + * + * @throws \Exception + */ + public function delete(ClientApiRequest $request, Server $server, Schedule $schedule, Task $task): JsonResponse + { + if ($task->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) { + throw new NotFoundHttpException(); + } + + if (!$request->user()->can(Permission::ACTION_SCHEDULE_UPDATE, $server)) { + throw new HttpForbiddenException('You do not have permission to perform this action.'); + } + + $schedule->tasks() + ->where('sequence_id', '>', $task->sequence_id) + ->decrement('sequence_id'); + $task->delete(); + + Activity::event('server:task.delete')->subject($schedule, $task)->property('name', $schedule->name)->log(); + + return new JsonResponse(null, Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/ServerController.php b/app/Http/Controllers/Api/Client/Servers/ServerController.php index ce4502e3a..ac898e2b6 100644 --- a/app/Http/Controllers/Api/Client/Servers/ServerController.php +++ b/app/Http/Controllers/Api/Client/Servers/ServerController.php @@ -4,22 +4,32 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Pterodactyl\Models\Server; use Pterodactyl\Transformers\Api\Client\ServerTransformer; +use Pterodactyl\Services\Servers\GetUserPermissionsService; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest; class ServerController extends ClientApiController { + /** + * ServerController constructor. + */ + public function __construct(private GetUserPermissionsService $permissionsService) + { + parent::__construct(); + } + /** * Transform an individual server into a response that can be consumed by a * client using the API. - * - * @param \Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest $request - * @return array */ - public function index(GetServerRequest $request): array + public function index(GetServerRequest $request, Server $server): array { - return $this->fractal->item($request->getModel(Server::class)) - ->transformWith($this->getTransformer(ServerTransformer::class)) + return $this->fractal->item($server) + ->transformWith(ServerTransformer::class) + ->addMeta([ + 'is_server_owner' => $request->user()->id === $server->owner_id, + 'user_permissions' => $this->permissionsService->handle($server, $request->user()), + ]) ->toArray(); } } diff --git a/app/Http/Controllers/Api/Client/Servers/SettingsController.php b/app/Http/Controllers/Api/Client/Servers/SettingsController.php new file mode 100644 index 000000000..b939f1680 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/SettingsController.php @@ -0,0 +1,95 @@ +input('name'); + $description = $request->has('description') ? (string) $request->input('description') : $server->description; + $this->repository->update($server->id, [ + 'name' => $name, + 'description' => $description, + ]); + + if ($server->name !== $name) { + Activity::event('server:settings.rename') + ->property(['old' => $server->name, 'new' => $name]) + ->log(); + } + + if ($server->description !== $description) { + Activity::event('server:settings.description') + ->property(['old' => $server->description, 'new' => $description]) + ->log(); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Reinstalls the server on the daemon. + * + * @throws \Throwable + */ + public function reinstall(ReinstallServerRequest $request, Server $server): JsonResponse + { + $this->reinstallServerService->handle($server); + + Activity::event('server:reinstall')->log(); + + return new JsonResponse([], Response::HTTP_ACCEPTED); + } + + /** + * Changes the Docker image in use by the server. + * + * @throws \Throwable + */ + public function dockerImage(SetDockerImageRequest $request, Server $server): JsonResponse + { + if (!in_array($server->image, array_values($server->egg->docker_images))) { + throw new BadRequestHttpException('This server\'s Docker image has been manually set by an administrator and cannot be updated.'); + } + + $original = $server->image; + $server->forceFill(['image' => $request->input('docker_image')])->saveOrFail(); + + if ($original !== $server->image) { + Activity::event('server:startup.image') + ->property(['old' => $original, 'new' => $request->input('docker_image')]) + ->log(); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/StartupController.php b/app/Http/Controllers/Api/Client/Servers/StartupController.php new file mode 100644 index 000000000..56b632617 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/StartupController.php @@ -0,0 +1,100 @@ +startupCommandService->handle($server); + + return $this->fractal->collection( + $server->variables()->where('user_viewable', true)->get() + ) + ->transformWith(EggVariableTransformer::class) + ->addMeta([ + 'startup_command' => $startup, + 'docker_images' => $server->egg->docker_images, + 'raw_startup_command' => $server->startup, + ]) + ->toArray(); + } + + /** + * Updates a single variable for a server. + * + * @throws \Illuminate\Validation\ValidationException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateStartupVariableRequest $request, Server $server): array + { + $variable = $server->variables()->where('env_variable', $request->input('key'))->first(); + $original = $variable->server_value; + + if (is_null($variable) || !$variable->user_viewable) { + throw new BadRequestHttpException('The environment variable you are trying to edit does not exist.'); + } elseif (!$variable->user_editable) { + throw new BadRequestHttpException('The environment variable you are trying to edit is read-only.'); + } + + /* @var \Pterodactyl\Models\EggVariable $variable */ + + // Revalidate the variable value using the egg variable specific validation rules for it. + $this->validate($request, ['value' => $variable->rules]); + + $this->repository->updateOrCreate([ + 'server_id' => $server->id, + 'variable_id' => $variable->id, + ], [ + 'variable_value' => $request->input('value') ?? '', + ]); + + $variable = $variable->refresh(); + $variable->server_value = $request->input('value'); + + $startup = $this->startupCommandService->handle($server); + + if ($variable->env_variable !== $request->input('value')) { + Activity::event('server:startup.edit') + ->subject($variable) + ->property([ + 'variable' => $variable->env_variable, + 'old' => $original, + 'new' => $request->input('value'), + ]) + ->log(); + } + + return $this->fractal->item($variable) + ->transformWith(EggVariableTransformer::class) + ->addMeta([ + 'startup_command' => $startup, + 'raw_startup_command' => $server->startup, + ]) + ->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php new file mode 100644 index 000000000..ac402abb9 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -0,0 +1,185 @@ +fractal->collection($server->subusers) + ->transformWith(SubuserTransformer::class) + ->toArray(); + } + + /** + * Returns a single subuser associated with this server instance. + */ + public function view(GetSubuserRequest $request): array + { + $subuser = $request->attributes->get('subuser'); + + return $this->fractal->item($subuser) + ->transformWith(SubuserTransformer::class) + ->toArray(); + } + + /** + * Create a new subuser for the given server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException + * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException + * @throws \Throwable + */ + public function store(StoreSubuserRequest $request, Server $server): array + { + $response = $this->creationService->handle( + $server, + $request->input('email'), + $this->getDefaultPermissions($request) + ); + + Activity::event('server:subuser.create') + ->subject($response->user) + ->property(['email' => $request->input('email'), 'permissions' => $this->getDefaultPermissions($request)]) + ->log(); + + return $this->fractal->item($response) + ->transformWith(SubuserTransformer::class) + ->toArray(); + } + + /** + * Update a given subuser in the system for the server. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateSubuserRequest $request, Server $server): array + { + /** @var \Pterodactyl\Models\Subuser $subuser */ + $subuser = $request->attributes->get('subuser'); + + $permissions = $this->getDefaultPermissions($request); + $current = $subuser->permissions; + + sort($permissions); + sort($current); + + $log = Activity::event('server:subuser.update') + ->subject($subuser->user) + ->property([ + 'email' => $subuser->user->email, + 'old' => $current, + 'new' => $permissions, + 'revoked' => true, + ]); + + // Only update the database and hit up the Wings instance to invalidate JTI's if the permissions + // have actually changed for the user. + if ($permissions !== $current) { + $log->transaction(function ($instance) use ($request, $subuser, $server) { + $this->repository->update($subuser->id, [ + 'permissions' => $this->getDefaultPermissions($request), + ]); + + try { + $this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id); + } catch (DaemonConnectionException $exception) { + // Don't block this request if we can't connect to the Wings instance. Chances are it is + // offline and the token will be invalid once Wings boots back. + Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); + + $instance->property('revoked', false); + } + }); + } + + $log->reset(); + + return $this->fractal->item($subuser->refresh()) + ->transformWith(SubuserTransformer::class) + ->toArray(); + } + + /** + * Removes a subusers from a server's assignment. + */ + public function delete(DeleteSubuserRequest $request, Server $server): JsonResponse + { + /** @var \Pterodactyl\Models\Subuser $subuser */ + $subuser = $request->attributes->get('subuser'); + + $log = Activity::event('server:subuser.delete') + ->subject($subuser->user) + ->property('email', $subuser->user->email) + ->property('revoked', true); + + $log->transaction(function ($instance) use ($server, $subuser) { + $subuser->delete(); + + try { + $this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id); + } catch (DaemonConnectionException $exception) { + // Don't block this request if we can't connect to the Wings instance. + Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); + + $instance->property('revoked', false); + } + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Returns the default permissions for subusers and parses out any permissions + * that were passed that do not also exist in the internally tracked list of + * permissions. + */ + protected function getDefaultPermissions(Request $request): array + { + $allowed = Permission::permissions() + ->map(function ($value, $prefix) { + return array_map(function ($value) use ($prefix) { + return "$prefix.$value"; + }, array_keys($value['keys'])); + }) + ->flatten() + ->all(); + + $cleaned = array_intersect($request->input('permissions') ?? [], $allowed); + + return array_unique(array_merge($cleaned, [Permission::ACTION_WEBSOCKET_CONNECT])); + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php new file mode 100644 index 000000000..59b6f75d4 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php @@ -0,0 +1,73 @@ +user(); + if ($user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $server)) { + throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.'); + } + + $permissions = $this->permissionsService->handle($server, $user); + + $node = $server->node; + if (!is_null($server->transfer)) { + // Check if the user has permissions to receive transfer logs. + if (!in_array('admin.websocket.transfer', $permissions)) { + throw new HttpForbiddenException('You do not have permission to view server transfer logs.'); + } + + // Redirect the websocket request to the new node if the server has been archived. + if ($server->transfer->archived) { + $node = $server->transfer->newNode; + } + } + + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(10)) + ->setUser($request->user()) + ->setClaims([ + 'server_uuid' => $server->uuid, + 'permissions' => $permissions, + ]) + ->handle($node, $user->id . $server->uuid); + + $socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $node->getConnectionAddress()); + + return new JsonResponse([ + 'data' => [ + 'token' => $token->toString(), + 'socket' => $socket . sprintf('/api/servers/%s/ws', $server->uuid), + ], + ]); + } +} diff --git a/app/Http/Controllers/Api/Client/TwoFactorController.php b/app/Http/Controllers/Api/Client/TwoFactorController.php new file mode 100644 index 000000000..8cfa09b6e --- /dev/null +++ b/app/Http/Controllers/Api/Client/TwoFactorController.php @@ -0,0 +1,101 @@ +user()->use_totp) { + throw new BadRequestHttpException('Two-factor authentication is already enabled on this account.'); + } + + return new JsonResponse([ + 'data' => $this->setupService->handle($request->user()), + ]); + } + + /** + * Updates a user's account to have two-factor enabled. + * + * @throws \Throwable + * @throws \Illuminate\Validation\ValidationException + */ + public function store(Request $request): JsonResponse + { + $validator = $this->validation->make($request->all(), [ + 'code' => ['required', 'string', 'size:6'], + 'password' => ['required', 'string'], + ]); + + $data = $validator->validate(); + if (!password_verify($data['password'], $request->user()->password)) { + throw new BadRequestHttpException('The password provided was not valid.'); + } + + $tokens = $this->toggleTwoFactorService->handle($request->user(), $data['code'], true); + + Activity::event('user:two-factor.create')->log(); + + return new JsonResponse([ + 'object' => 'recovery_tokens', + 'attributes' => [ + 'tokens' => $tokens, + ], + ]); + } + + /** + * Disables two-factor authentication on an account if the password provided + * is valid. + * + * @throws \Throwable + */ + public function delete(Request $request): JsonResponse + { + if (!password_verify($request->input('password') ?? '', $request->user()->password)) { + throw new BadRequestHttpException('The password provided was not valid.'); + } + + /** @var \Pterodactyl\Models\User $user */ + $user = $request->user(); + + $user->update([ + 'totp_authenticated_at' => Carbon::now(), + 'use_totp' => false, + ]); + + Activity::event('user:two-factor.delete')->log(); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/ActivityProcessingController.php b/app/Http/Controllers/Api/Remote/ActivityProcessingController.php new file mode 100644 index 000000000..b9d12c248 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/ActivityProcessingController.php @@ -0,0 +1,90 @@ +getTimezone(); + + /** @var \Pterodactyl\Models\Node $node */ + $node = $request->attributes->get('node'); + + $servers = $node->servers()->whereIn('uuid', $request->servers())->get()->keyBy('uuid'); + $users = User::query()->whereIn('uuid', $request->users())->get()->keyBy('uuid'); + + $logs = []; + foreach ($request->input('data') as $datum) { + /** @var \Pterodactyl\Models\Server|null $server */ + $server = $servers->get($datum['server']); + if (is_null($server) || !Str::startsWith($datum['event'], 'server:')) { + continue; + } + + try { + $when = Carbon::createFromFormat( + \DateTimeInterface::RFC3339, + preg_replace('/(\.\d+)Z$/', 'Z', $datum['timestamp']), + 'UTC' + ); + } catch (\Exception $exception) { + Log::warning($exception, ['timestamp' => $datum['timestamp']]); + + // If we cannot parse the value for some reason don't blow up this request, just go ahead + // and log the event with the current time, and set the metadata value to have the original + // timestamp that was provided. + $when = Carbon::now(); + $datum['metadata'] = array_merge($datum['metadata'] ?? [], ['original_timestamp' => $datum['timestamp']]); + } + + $log = [ + 'ip' => empty($datum['ip']) ? '127.0.0.1' : $datum['ip'], + 'event' => $datum['event'], + 'properties' => json_encode($datum['metadata'] ?? []), + // We have to change the time to the current timezone due to the way Laravel is handling + // the date casting internally. If we just leave it in UTC it ends up getting double-cast + // and the time is way off. + 'timestamp' => $when->setTimezone($tz), + ]; + + if ($user = $users->get($datum['user'])) { + $log['actor_id'] = $user->id; + $log['actor_type'] = $user->getMorphClass(); + } + + if (!isset($logs[$datum['server']])) { + $logs[$datum['server']] = []; + } + + $logs[$datum['server']][] = $log; + } + + foreach ($logs as $key => $data) { + Assert::isInstanceOf($server = $servers->get($key), Server::class); + + $batch = []; + foreach ($data as $datum) { + $id = ActivityLog::insertGetId($datum); + $batch[] = [ + 'activity_log_id' => $id, + 'subject_id' => $server->id, + 'subject_type' => $server->getMorphClass(), + ]; + } + + ActivityLogSubject::insert($batch); + } + } +} diff --git a/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php b/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php new file mode 100644 index 000000000..7d92e0b1a --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php @@ -0,0 +1,123 @@ +query('size'); + if (empty($size)) { + throw new BadRequestHttpException('A non-empty "size" query parameter must be provided.'); + } + + /** @var \Pterodactyl\Models\Backup $backup */ + $backup = Backup::query()->where('uuid', $backup)->firstOrFail(); + + // Prevent backups that have already been completed from trying to + // be uploaded again. + if (!is_null($backup->completed_at)) { + throw new ConflictHttpException('This backup is already in a completed state.'); + } + + // Ensure we are using the S3 adapter. + $adapter = $this->backupManager->adapter(); + if (!$adapter instanceof S3Filesystem) { + throw new BadRequestHttpException('The configured backup adapter is not an S3 compatible adapter.'); + } + + // The path where backup will be uploaded to + $path = sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid); + + // Get the S3 client + $client = $adapter->getClient(); + $expires = CarbonImmutable::now()->addMinutes(config('backups.presigned_url_lifespan', 60)); + + // Params for generating the presigned urls + $params = [ + 'Bucket' => $adapter->getBucket(), + 'Key' => $path, + 'ContentType' => 'application/x-gzip', + ]; + + $storageClass = config('backups.disks.s3.storage_class'); + if (!is_null($storageClass)) { + $params['StorageClass'] = $storageClass; + } + + // Execute the CreateMultipartUpload request + $result = $client->execute($client->getCommand('CreateMultipartUpload', $params)); + + // Get the UploadId from the CreateMultipartUpload request, this is needed to create + // the other presigned urls. + $params['UploadId'] = $result->get('UploadId'); + + // Retrieve configured part size + $maxPartSize = $this->getConfiguredMaxPartSize(); + + // Create as many UploadPart presigned urls as needed + $parts = []; + for ($i = 0; $i < ($size / $maxPartSize); ++$i) { + $parts[] = $client->createPresignedRequest( + $client->getCommand('UploadPart', array_merge($params, ['PartNumber' => $i + 1])), + $expires + )->getUri()->__toString(); + } + + // Set the upload_id on the backup in the database. + $backup->update(['upload_id' => $params['UploadId']]); + + return new JsonResponse([ + 'parts' => $parts, + 'part_size' => $maxPartSize, + ]); + } + + /** + * Get the configured maximum size of a single part in the multipart upload. + * + * The function tries to retrieve a configured value from the configuration. + * If no value is specified, a fallback value will be used. + * + * Note if the received config cannot be converted to int (0), is zero or is negative, + * the fallback value will be used too. + * + * The fallback value is {@see BackupRemoteUploadController::DEFAULT_MAX_PART_SIZE}. + */ + private function getConfiguredMaxPartSize(): int + { + $maxPartSize = (int) config('backups.max_part_size', self::DEFAULT_MAX_PART_SIZE); + if ($maxPartSize <= 0) { + $maxPartSize = self::DEFAULT_MAX_PART_SIZE; + } + + return $maxPartSize; + } +} diff --git a/app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php b/app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php new file mode 100644 index 000000000..f9c2a7932 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php @@ -0,0 +1,146 @@ +where('uuid', $backup)->firstOrFail(); + + if ($model->is_successful) { + throw new BadRequestHttpException('Cannot update the status of a backup that is already marked as completed.'); + } + + $action = $request->boolean('successful') ? 'server:backup.complete' : 'server:backup.fail'; + $log = Activity::event($action)->subject($model, $model->server)->property('name', $model->name); + + $log->transaction(function () use ($model, $request) { + $successful = $request->boolean('successful'); + + $model->fill([ + 'is_successful' => $successful, + // Change the lock state to unlocked if this was a failed backup so that it can be + // deleted easily. Also does not make sense to have a locked backup on the system + // that is failed. + 'is_locked' => $successful ? $model->is_locked : false, + 'checksum' => $successful ? ($request->input('checksum_type') . ':' . $request->input('checksum')) : null, + 'bytes' => $successful ? $request->input('size') : 0, + 'completed_at' => CarbonImmutable::now(), + ])->save(); + + // Check if we are using the s3 backup adapter. If so, make sure we mark the backup as + // being completed in S3 correctly. + $adapter = $this->backupManager->adapter(); + if ($adapter instanceof S3Filesystem) { + $this->completeMultipartUpload($model, $adapter, $successful, $request->input('parts')); + } + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Handles toggling the restoration status of a server. The server status field should be + * set back to null, even if the restoration failed. This is not an unsolvable state for + * the server, and the user can keep trying to restore, or just use the reinstall button. + * + * The only thing the successful field does is update the entry value for the audit logs + * table tracking for this restoration. + * + * @throws \Throwable + */ + public function restore(Request $request, string $backup): JsonResponse + { + /** @var \Pterodactyl\Models\Backup $model */ + $model = Backup::query()->where('uuid', $backup)->firstOrFail(); + + $model->server->update(['status' => null]); + + Activity::event($request->boolean('successful') ? 'server:backup.restore-complete' : 'server.backup.restore-failed') + ->subject($model, $model->server) + ->property('name', $model->name) + ->log(); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Marks a multipart upload in a given S3-compatible instance as failed or successful for + * the given backup. + * + * @throws \Exception + * @throws \Pterodactyl\Exceptions\DisplayException + */ + protected function completeMultipartUpload(Backup $backup, S3Filesystem $adapter, bool $successful, ?array $parts): void + { + // This should never really happen, but if it does don't let us fall victim to Amazon's + // wildly fun error messaging. Just stop the process right here. + if (empty($backup->upload_id)) { + // A failed backup doesn't need to error here, this can happen if the backup encounters + // an error before we even start the upload. AWS gives you tooling to clear these failed + // multipart uploads as needed too. + if (!$successful) { + return; + } + + throw new DisplayException('Cannot complete backup request: no upload_id present on model.'); + } + + $params = [ + 'Bucket' => $adapter->getBucket(), + 'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid), + 'UploadId' => $backup->upload_id, + ]; + + $client = $adapter->getClient(); + if (!$successful) { + $client->execute($client->getCommand('AbortMultipartUpload', $params)); + + return; + } + + // Otherwise send a CompleteMultipartUpload request. + $params['MultipartUpload'] = [ + 'Parts' => [], + ]; + + if (is_null($parts)) { + $params['MultipartUpload']['Parts'] = $client->execute($client->getCommand('ListParts', $params))['Parts']; + } else { + foreach ($parts as $part) { + $params['MultipartUpload']['Parts'][] = [ + 'ETag' => $part['etag'], + 'PartNumber' => $part['part_number'], + ]; + } + } + + $client->execute($client->getCommand('CompleteMultipartUpload', $params)); + } +} diff --git a/app/Http/Controllers/Api/Remote/EggInstallController.php b/app/Http/Controllers/Api/Remote/EggInstallController.php index 65aedfbff..31df7a96c 100644 --- a/app/Http/Controllers/Api/Remote/EggInstallController.php +++ b/app/Http/Controllers/Api/Remote/EggInstallController.php @@ -10,36 +10,17 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class EggInstallController extends Controller { - /** - * @var \Pterodactyl\Services\Servers\EnvironmentService - */ - private $environment; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - /** * EggInstallController constructor. - * - * @param \Pterodactyl\Services\Servers\EnvironmentService $environment - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ - public function __construct(EnvironmentService $environment, ServerRepositoryInterface $repository) + public function __construct(private EnvironmentService $environment, private ServerRepositoryInterface $repository) { - $this->environment = $environment; - $this->repository = $repository; } /** * Handle request to get script and installation information for a server * that is being created on the node. * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @return \Illuminate\Http\JsonResponse - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function index(Request $request, string $uuid): JsonResponse @@ -57,7 +38,7 @@ class EggInstallController extends Controller return response()->json([ 'scripts' => [ - 'install' => ! $egg->copy_script_install ? null : str_replace(["\r\n", "\n", "\r"], "\n", $egg->copy_script_install), + 'install' => !$egg->copy_script_install ? null : str_replace(["\r\n", "\n", "\r"], "\n", $egg->copy_script_install), 'privileged' => $egg->script_is_privileged, ], 'config' => [ diff --git a/app/Http/Controllers/Api/Remote/EggRetrievalController.php b/app/Http/Controllers/Api/Remote/EggRetrievalController.php deleted file mode 100644 index 2f006b64e..000000000 --- a/app/Http/Controllers/Api/Remote/EggRetrievalController.php +++ /dev/null @@ -1,74 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Api\Remote; - -use Illuminate\Http\JsonResponse; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Eggs\EggConfigurationService; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; - -class EggRetrievalController extends Controller -{ - /** - * @var \Pterodactyl\Services\Eggs\EggConfigurationService - */ - protected $configurationFileService; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - - /** - * OptionUpdateController constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository - * @param \Pterodactyl\Services\Eggs\EggConfigurationService $configurationFileService - */ - public function __construct( - EggRepositoryInterface $repository, - EggConfigurationService $configurationFileService - ) { - $this->configurationFileService = $configurationFileService; - $this->repository = $repository; - } - - /** - * Return a JSON array of Eggs and the SHA1 hash of their configuration file. - * - * @return \Illuminate\Http\JsonResponse - */ - public function index(): JsonResponse - { - $eggs = $this->repository->getAllWithCopyAttributes(); - - $response = []; - $eggs->each(function ($egg) use (&$response) { - $response[$egg->uuid] = sha1(json_encode($this->configurationFileService->handle($egg))); - }); - - return response()->json($response); - } - - /** - * Return the configuration file for a single Egg for the Daemon. - * - * @param string $uuid - * @return \Illuminate\Http\JsonResponse - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function download(string $uuid): JsonResponse - { - $option = $this->repository->getWithCopyAttributes($uuid, 'uuid'); - - return response()->json($this->configurationFileService->handle($option)); - } -} diff --git a/app/Http/Controllers/Api/Remote/FileDownloadController.php b/app/Http/Controllers/Api/Remote/FileDownloadController.php deleted file mode 100644 index fa4818fc9..000000000 --- a/app/Http/Controllers/Api/Remote/FileDownloadController.php +++ /dev/null @@ -1,50 +0,0 @@ -cache = $cache; - } - - /** - * Handle a request to authenticate a download using a token and return - * the path of the file to the daemon. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - */ - public function index(Request $request): JsonResponse - { - $download = $this->cache->pull('Server:Downloads:' . $request->input('token', '')); - - if (is_null($download)) { - throw new NotFoundHttpException('No file was found using the token provided.'); - } - - return response()->json([ - 'path' => array_get($download, 'path'), - 'server' => array_get($download, 'server'), - ]); - } -} diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php b/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php new file mode 100644 index 000000000..8d8b0de92 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php @@ -0,0 +1,121 @@ +repository->getByUuid($uuid); + + return new JsonResponse([ + 'settings' => $this->configurationStructureService->handle($server), + 'process_configuration' => $this->eggConfigurationService->handle($server), + ]); + } + + /** + * Lists all servers with their configurations that are assigned to the requesting node. + */ + public function list(Request $request): ServerConfigurationCollection + { + /** @var \Pterodactyl\Models\Node $node */ + $node = $request->attributes->get('node'); + + // Avoid run-away N+1 SQL queries by preloading the relationships that are used + // within each of the services called below. + $servers = Server::query()->with('allocations', 'egg', 'mounts', 'variables', 'location') + ->where('node_id', $node->id) + // If you don't cast this to a string you'll end up with a stringified per_page returned in + // the metadata, and then Wings will panic crash as a result. + ->paginate((int) $request->input('per_page', 50)); + + return new ServerConfigurationCollection($servers); + } + + /** + * Resets the state of all servers on the node to be normal. This is triggered + * when Wings restarts and is useful for ensuring that any servers on the node + * do not get incorrectly stuck in installing/restoring from backup states since + * a Wings reboot would completely stop those processes. + * + * @throws \Throwable + */ + public function resetState(Request $request): JsonResponse + { + $node = $request->attributes->get('node'); + + // Get all the servers that are currently marked as restoring from a backup + // on this node that do not have a failed backup tracked in the audit logs table + // as well. + // + // For each of those servers we'll track a new audit log entry to mark them as + // failed and then update them all to be in a valid state. + $servers = Server::query() + ->with([ + 'activity' => fn ($builder) => $builder + ->where('activity_logs.event', 'server:backup.restore-started') + ->latest('timestamp'), + ]) + ->where('node_id', $node->id) + ->where('status', Server::STATUS_RESTORING_BACKUP) + ->get(); + + $this->connection->transaction(function () use ($node, $servers) { + /** @var \Pterodactyl\Models\Server $server */ + foreach ($servers as $server) { + /** @var \Pterodactyl\Models\ActivityLog|null $activity */ + $activity = $server->activity->first(); + if (!is_null($activity)) { + if ($subject = $activity->subjects->where('subject_type', 'backup')->first()) { + // Just create a new audit entry for this event and update the server state + // so that power actions, file management, and backups can resume as normal. + /** @var Backup $actualSubject */ + $actualSubject = $subject->subject; + Activity::event('server:backup.restore-failed') + ->subject($server, $actualSubject) + ->property('name', $actualSubject->name) + ->log(); + } + } + } + + // Update any server marked as installing or restoring as being in a normal state + // at this point in the process. + Server::query()->where('node_id', $node->id) + ->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP]) + ->update(['status' => null]); + }); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php b/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php new file mode 100644 index 000000000..8fcfbe064 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php @@ -0,0 +1,80 @@ +repository->getByUuid($uuid); + $egg = $server->egg; + + return new JsonResponse([ + 'container_image' => $egg->copy_script_container, + 'entrypoint' => $egg->copy_script_entry, + 'script' => $egg->copy_script_install, + ]); + } + + /** + * Updates the installation state of a server. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(InstallationDataRequest $request, string $uuid): JsonResponse + { + $server = $this->repository->getByUuid($uuid); + $status = null; + + // Make sure the type of failure is accurate + if (!$request->boolean('successful')) { + $status = Server::STATUS_INSTALL_FAILED; + + if ($request->boolean('reinstall')) { + $status = Server::STATUS_REINSTALL_FAILED; + } + } + + // Keep the server suspended if it's already suspended + if ($server->status === Server::STATUS_SUSPENDED) { + $status = Server::STATUS_SUSPENDED; + } + + $this->repository->update($server->id, ['status' => $status, 'installed_at' => CarbonImmutable::now()], true, true); + + // If the server successfully installed, fire installed event. + // This logic allows individually disabling install and reinstall notifications separately. + $isInitialInstall = is_null($server->installed_at); + if ($isInitialInstall && config()->get('pterodactyl.email.send_install_notification', true)) { + $this->eventDispatcher->dispatch(new ServerInstalled($server)); + } elseif (!$isInitialInstall && config()->get('pterodactyl.email.send_reinstall_notification', true)) { + $this->eventDispatcher->dispatch(new ServerInstalled($server)); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php new file mode 100644 index 000000000..c14ddea97 --- /dev/null +++ b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php @@ -0,0 +1,107 @@ +repository->getByUuid($uuid); + $transfer = $server->transfer; + if (is_null($transfer)) { + throw new ConflictHttpException('Server is not being transferred.'); + } + + return $this->processFailedTransfer($transfer); + } + + /** + * The daemon notifies us about a transfer success. + * + * @throws \Throwable + */ + public function success(string $uuid): JsonResponse + { + $server = $this->repository->getByUuid($uuid); + $transfer = $server->transfer; + if (is_null($transfer)) { + throw new ConflictHttpException('Server is not being transferred.'); + } + + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->connection->transaction(function () use ($server, $transfer) { + $allocations = array_merge([$transfer->old_allocation], $transfer->old_additional_allocations); + + // Remove the old allocations for the server and re-assign the server to the new + // primary allocation and node. + Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]); + $server->update([ + 'allocation_id' => $transfer->new_allocation, + 'node_id' => $transfer->new_node, + ]); + + $server = $server->fresh(); + $server->transfer->update(['successful' => true]); + + return $server; + }); + + // Delete the server from the old node making sure to point it to the old node so + // that we do not delete it from the new node the server was transferred to. + try { + $this->daemonServerRepository + ->setServer($server) + ->setNode($transfer->oldNode) + ->delete(); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['transfer_id' => $server->transfer->id]); + } + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + + /** + * Release all the reserved allocations for this transfer and mark it as failed in + * the database. + * + * @throws \Throwable + */ + protected function processFailedTransfer(ServerTransfer $transfer): JsonResponse + { + $this->connection->transaction(function () use (&$transfer) { + $transfer->forceFill(['successful' => false])->saveOrFail(); + + $allocations = array_merge([$transfer->new_allocation], $transfer->new_additional_allocations); + Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]); + }); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Api/Remote/SftpAuthenticationController.php b/app/Http/Controllers/Api/Remote/SftpAuthenticationController.php new file mode 100644 index 000000000..a360d1d0e --- /dev/null +++ b/app/Http/Controllers/Api/Remote/SftpAuthenticationController.php @@ -0,0 +1,165 @@ +parseUsername($request->input('username')); + if (empty($connection['server'])) { + throw new BadRequestHttpException('No valid server identifier was included in the request.'); + } + + if ($this->hasTooManyLoginAttempts($request)) { + $seconds = $this->limiter()->availableIn($this->throttleKey($request)); + + throw new TooManyRequestsHttpException($seconds, "Too many login attempts for this account, please try again in $seconds seconds."); + } + + $user = $this->getUser($request, $connection['username']); + $server = $this->getServer($request, $connection['server']); + + if ($request->input('type') !== 'public_key') { + if (!password_verify($request->input('password'), $user->password)) { + Activity::event('auth:sftp.fail')->property('method', 'password')->subject($user)->log(); + + $this->reject($request); + } + } else { + $key = null; + try { + $key = PublicKeyLoader::loadPublicKey(trim($request->input('password'))); + } catch (NoKeyLoadedException) { + // do nothing + } + + if (!$key || !$user->sshKeys()->where('fingerprint', $key->getFingerprint('sha256'))->exists()) { + // We don't log here because of the way the SFTP system works. This endpoint + // will get hit for every key the user provides, which could be 4 or 5. That is + // a lot of unnecessary log noise. + // + // For now, we'll only log failures due to a bad password as those are not likely + // to occur more than once in a session for the user, and are more likely to be of + // value to the end user. + $this->reject($request, is_null($key)); + } + } + + $this->validateSftpAccess($user, $server); + + return new JsonResponse([ + 'user' => $user->uuid, + 'server' => $server->uuid, + 'permissions' => $this->permissions->handle($server, $user), + ]); + } + + /** + * Finds the server being requested and ensures that it belongs to the node this + * request stems from. + */ + protected function getServer(Request $request, string $uuid): Server + { + return Server::query() + ->where(fn ($builder) => $builder->where('uuid', $uuid)->orWhere('uuidShort', $uuid)) + ->where('node_id', $request->attributes->get('node')->id) + ->firstOr(function () use ($request) { + $this->reject($request); + }); + } + + /** + * Finds a user with the given username or increments the login attempts. + */ + protected function getUser(Request $request, string $username): User + { + return User::query()->where('username', $username)->firstOr(function () use ($request) { + $this->reject($request); + }); + } + + /** + * Parses the username provided to the request. + * + * @return array{"username": string, "server": string} + */ + protected function parseUsername(string $value): array + { + // Reverse the string to avoid issues with usernames that contain periods. + $parts = explode('.', strrev($value), 2); + + // Unreverse the strings after parsing them apart. + return [ + 'username' => strrev(array_get($parts, 1)), + 'server' => strrev(array_get($parts, 0)), + ]; + } + + /** + * Rejects the request and increments the login attempts. + */ + protected function reject(Request $request, bool $increment = true): void + { + if ($increment) { + $this->incrementLoginAttempts($request); + } + + throw new HttpForbiddenException('Authorization credentials were not correct, please try again.'); + } + + /** + * Validates that a user should have permission to use SFTP for the given server. + */ + protected function validateSftpAccess(User $user, Server $server): void + { + if (!$user->root_admin && $server->owner_id !== $user->id) { + $permissions = $this->permissions->handle($server, $user); + + if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) { + Activity::event('server:sftp.denied')->actor($user)->subject($server)->log(); + + throw new HttpForbiddenException('You do not have permission to access SFTP for this server.'); + } + } + + $server->validateCurrentState(); + } + + /** + * Get the throttle key for the given request. + */ + protected function throttleKey(Request $request): string + { + $username = explode('.', strrev($request->input('username', ''))); + + return strtolower(strrev($username[0] ?? '') . '|' . $request->ip()); + } +} diff --git a/app/Http/Controllers/Api/Remote/SftpController.php b/app/Http/Controllers/Api/Remote/SftpController.php deleted file mode 100644 index 083544237..000000000 --- a/app/Http/Controllers/Api/Remote/SftpController.php +++ /dev/null @@ -1,91 +0,0 @@ -authenticationService = $authenticationService; - } - - /** - * Authenticate a set of credentials and return the associated server details - * for a SFTP connection on the daemon. - * - * @param \Pterodactyl\Http\Requests\Api\Remote\SftpAuthenticationFormRequest $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function index(SftpAuthenticationFormRequest $request): JsonResponse - { - $parts = explode('.', strrev($request->input('username')), 2); - $connection = [ - 'username' => strrev(array_get($parts, 1)), - 'server' => strrev(array_get($parts, 0)), - ]; - - $this->incrementLoginAttempts($request); - - if ($this->hasTooManyLoginAttempts($request)) { - return response()->json([ - 'error' => 'Logins throttled.', - ], Response::HTTP_TOO_MANY_REQUESTS); - } - - try { - $data = $this->authenticationService->handle( - $connection['username'], - $request->input('password'), - object_get($request->attributes->get('node'), 'id', 0), - empty($connection['server']) ? null : $connection['server'] - ); - - $this->clearLoginAttempts($request); - } catch (BadRequestHttpException $exception) { - return response()->json([ - 'error' => 'The server you are trying to access is not installed or is suspended.', - ], Response::HTTP_BAD_REQUEST); - } catch (RecordNotFoundException $exception) { - return response()->json([ - 'error' => 'Unable to locate a resource using the username and password provided.', - ], Response::HTTP_NOT_FOUND); - } - - return response()->json($data); - } - - /** - * Get the throttle key for the given request. - * - * @param \Illuminate\Http\Request $request - * @return string - */ - protected function throttleKey(Request $request) - { - return strtolower(array_get(explode('.', $request->input('username')), 0) . '|' . $request->ip()); - } -} diff --git a/app/Http/Controllers/Api/Remote/ValidateKeyController.php b/app/Http/Controllers/Api/Remote/ValidateKeyController.php deleted file mode 100644 index 86a02cbce..000000000 --- a/app/Http/Controllers/Api/Remote/ValidateKeyController.php +++ /dev/null @@ -1,100 +0,0 @@ -. - * - * 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. - */ - -namespace Pterodactyl\Http\Controllers\Api\Remote; - -use Spatie\Fractal\Fractal; -use Illuminate\Http\Response; -use Pterodactyl\Http\Controllers\Controller; -use Illuminate\Contracts\Foundation\Application; -use Illuminate\Foundation\Testing\HttpException; -use League\Fractal\Serializer\JsonApiSerializer; -use Pterodactyl\Transformers\Daemon\ApiKeyTransformer; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; - -class ValidateKeyController extends Controller -{ - /** - * @var \Illuminate\Contracts\Foundation\Application - */ - protected $app; - - /** - * @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface - */ - protected $daemonKeyRepository; - - /** - * @var \Spatie\Fractal\Fractal - */ - protected $fractal; - - /** - * ValidateKeyController constructor. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $daemonKeyRepository - * @param \Spatie\Fractal\Fractal $fractal - */ - public function __construct( - Application $app, - DaemonKeyRepositoryInterface $daemonKeyRepository, - Fractal $fractal - ) { - $this->app = $app; - $this->daemonKeyRepository = $daemonKeyRepository; - $this->fractal = $fractal; - } - - /** - * Return the server(s) and permissions associated with an API key. - * - * @param string $token - * @return array - * - * @throws \Illuminate\Foundation\Testing\HttpException - */ - public function index($token) - { - if (! starts_with($token, DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER)) { - throw new HttpException(Response::HTTP_NOT_IMPLEMENTED); - } - - try { - $key = $this->daemonKeyRepository->getKeyWithServer($token); - } catch (RecordNotFoundException $exception) { - throw new NotFoundHttpException; - } - - if ($key->getRelation('server')->suspended || $key->getRelation('server')->installed !== 1) { - throw new NotFoundHttpException; - } - - return $this->fractal->item($key, $this->app->make(ApiKeyTransformer::class), 'server') - ->serializeWith(JsonApiSerializer::class) - ->toArray(); - } -} diff --git a/app/Http/Controllers/Auth/AbstractLoginController.php b/app/Http/Controllers/Auth/AbstractLoginController.php new file mode 100644 index 000000000..c76a5afc2 --- /dev/null +++ b/app/Http/Controllers/Auth/AbstractLoginController.php @@ -0,0 +1,108 @@ +lockoutTime = config('auth.lockout.time'); + $this->maxLoginAttempts = config('auth.lockout.attempts'); + $this->auth = Container::getInstance()->make(AuthManager::class); + } + + /** + * Get the failed login response instance. + * + * @return never + * + * @throws DisplayException + */ + protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null, string $message = null) + { + $this->incrementLoginAttempts($request); + $this->fireFailedLoginEvent($user, [ + $this->getField($request->input('user')) => $request->input('user'), + ]); + + if ($request->route()->named('auth.login-checkpoint')) { + throw new DisplayException($message ?? trans('auth.two_factor.checkpoint_failed')); + } + + throw new DisplayException(trans('auth.failed')); + } + + /** + * Send the response after the user was authenticated. + */ + protected function sendLoginResponse(User $user, Request $request): JsonResponse + { + $request->session()->remove('auth_confirmation_token'); + $request->session()->regenerate(); + + $this->clearLoginAttempts($request); + + $this->auth->guard()->login($user, true); + + Event::dispatch(new DirectLogin($user, true)); + + return new JsonResponse([ + 'data' => [ + 'complete' => true, + 'intended' => $this->redirectPath(), + 'user' => $user->toReactObject(), + ], + ]); + } + + /** + * Determine if the user is logging in using an email or username. + */ + protected function getField(string $input = null): string + { + return ($input && str_contains($input, '@')) ? 'email' : 'username'; + } + + /** + * Fire a failed login event. + */ + protected function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = []) + { + Event::dispatch(new Failed('auth', $user, $credentials)); + } +} diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 896489f97..d67b0de51 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -3,7 +3,7 @@ namespace Pterodactyl\Http\Controllers\Auth; use Illuminate\Http\Request; -use Illuminate\Http\RedirectResponse; +use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Password; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Events\Auth\FailedPasswordReset; @@ -15,12 +15,8 @@ class ForgotPasswordController extends Controller /** * Get the response for a failed password reset link. - * - * @param \Illuminate\Http\Request - * @param string $response - * @return \Illuminate\Http\RedirectResponse */ - protected function sendResetLinkFailedResponse(Request $request, $response): RedirectResponse + protected function sendResetLinkFailedResponse(Request $request, $response): JsonResponse { // As noted in #358 we will return success even if it failed // to avoid pointing out that an account does or does not @@ -29,4 +25,16 @@ class ForgotPasswordController extends Controller return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT); } + + /** + * Get the response for a successful password reset link. + * + * @param string $response + */ + protected function sendResetLinkResponse(Request $request, $response): JsonResponse + { + return response()->json([ + 'status' => trans($response), + ]); + } } diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php new file mode 100644 index 000000000..82580edc0 --- /dev/null +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -0,0 +1,131 @@ +hasTooManyLoginAttempts($request)) { + $this->sendLockoutResponse($request); + } + + $details = $request->session()->get('auth_confirmation_token'); + if (!$this->hasValidSessionData($details)) { + $this->sendFailedLoginResponse($request, null, self::TOKEN_EXPIRED_MESSAGE); + } + + if (!hash_equals($request->input('confirmation_token') ?? '', $details['token_value'])) { + $this->sendFailedLoginResponse($request); + } + + try { + /** @var \Pterodactyl\Models\User $user */ + $user = User::query()->findOrFail($details['user_id']); + } catch (ModelNotFoundException) { + $this->sendFailedLoginResponse($request, null, self::TOKEN_EXPIRED_MESSAGE); + } + + // Recovery tokens go through a slightly different pathway for usage. + if (!is_null($recoveryToken = $request->input('recovery_token'))) { + if ($this->isValidRecoveryToken($user, $recoveryToken)) { + Event::dispatch(new ProvidedAuthenticationToken($user, true)); + + return $this->sendLoginResponse($user, $request); + } + } else { + $decrypted = $this->encrypter->decrypt($user->totp_secret); + + if ($this->google2FA->verifyKey($decrypted, $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) { + Event::dispatch(new ProvidedAuthenticationToken($user)); + + return $this->sendLoginResponse($user, $request); + } + } + + $this->sendFailedLoginResponse($request, $user, !empty($recoveryToken) ? 'The recovery token provided is not valid.' : null); + } + + /** + * Determines if a given recovery token is valid for the user account. If we find a matching token + * it will be deleted from the database. + * + * @throws \Exception + */ + protected function isValidRecoveryToken(User $user, string $value): bool + { + foreach ($user->recoveryTokens as $token) { + if (password_verify($value, $token->token)) { + $token->delete(); + + return true; + } + } + + return false; + } + + /** + * Determines if the data provided from the session is valid or not. This + * will return false if the data is invalid, or if more time has passed than + * was configured when the session was written. + */ + protected function hasValidSessionData(array $data): bool + { + $validator = $this->validation->make($data, [ + 'user_id' => 'required|integer|min:1', + 'token_value' => 'required|string', + 'expires_at' => 'required', + ]); + + if ($validator->fails()) { + return false; + } + + if (!$data['expires_at'] instanceof CarbonInterface) { + return false; + } + + if ($data['expires_at']->isBefore(CarbonImmutable::now())) { + return false; + } + + return true; + } +} diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index d5141c8d2..49a17378d 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -2,254 +2,74 @@ namespace Pterodactyl\Http\Controllers\Auth; +use Carbon\CarbonImmutable; +use Illuminate\Support\Str; use Illuminate\Http\Request; -use Illuminate\Auth\AuthManager; -use PragmaRX\Google2FA\Google2FA; -use Illuminate\Auth\Events\Failed; -use Illuminate\Http\RedirectResponse; -use Pterodactyl\Http\Controllers\Controller; -use Illuminate\Contracts\Auth\Authenticatable; -use Illuminate\Contracts\Encryption\Encrypter; -use Illuminate\Foundation\Auth\AuthenticatesUsers; -use Illuminate\Contracts\Cache\Repository as CacheRepository; -use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Illuminate\Contracts\Config\Repository as ConfigRepository; +use Pterodactyl\Models\User; +use Illuminate\Http\JsonResponse; +use Pterodactyl\Facades\Activity; +use Illuminate\Contracts\View\View; +use Illuminate\Database\Eloquent\ModelNotFoundException; -class LoginController extends Controller +class LoginController extends AbstractLoginController { - use AuthenticatesUsers; - /** - * @var \Illuminate\Auth\AuthManager + * Handle all incoming requests for the authentication routes and render the + * base authentication view component. React will take over at this point and + * turn the login area into an SPA. */ - private $auth; - - /** - * @var \Illuminate\Contracts\Cache\Repository - */ - private $cache; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - private $config; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $repository; - - /** - * @var \PragmaRX\Google2FA\Google2FA - */ - private $google2FA; - - /** - * Where to redirect users after login / registration. - * - * @var string - */ - protected $redirectTo = '/'; - - /** - * Lockout time for failed login requests. - * - * @var int - */ - protected $decayMinutes; - - /** - * After how many attempts should logins be throttled and locked. - * - * @var int - */ - protected $maxAttempts; - - /** - * LoginController constructor. - * - * @param \Illuminate\Auth\AuthManager $auth - * @param \Illuminate\Contracts\Cache\Repository $cache - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - * @param \PragmaRX\Google2FA\Google2FA $google2FA - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository - */ - public function __construct( - AuthManager $auth, - CacheRepository $cache, - ConfigRepository $config, - Encrypter $encrypter, - Google2FA $google2FA, - UserRepositoryInterface $repository - ) { - $this->auth = $auth; - $this->cache = $cache; - $this->config = $config; - $this->encrypter = $encrypter; - $this->google2FA = $google2FA; - $this->repository = $repository; - - $this->decayMinutes = $this->config->get('auth.lockout.time'); - $this->maxAttempts = $this->config->get('auth.lockout.attempts'); + public function index(): View + { + return view('templates/auth.core'); } /** * Handle a login request to the application. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response - * + * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Illuminate\Validation\ValidationException */ - public function login(Request $request) + public function login(Request $request): JsonResponse { - $username = $request->input($this->username()); - $useColumn = $this->getField($username); - if ($this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); $this->sendLockoutResponse($request); } try { - $user = $this->repository->findFirstWhere([[$useColumn, '=', $username]]); - } catch (RecordNotFoundException $exception) { - return $this->sendFailedLoginResponse($request); + $username = $request->input('user'); + + /** @var \Pterodactyl\Models\User $user */ + $user = User::query()->where($this->getField($username), $username)->firstOrFail(); + } catch (ModelNotFoundException) { + $this->sendFailedLoginResponse($request); } - if (! password_verify($request->input('password'), $user->password)) { - return $this->sendFailedLoginResponse($request, $user); + // Ensure that the account is using a valid username and password before trying to + // continue. Previously this was handled in the 2FA checkpoint, however that has + // a flaw in which you can discover if an account exists simply by seeing if you + // can proceed to the next step in the login process. + if (!password_verify($request->input('password'), $user->password)) { + $this->sendFailedLoginResponse($request, $user); } - if ($user->use_totp) { - $token = str_random(64); - $this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => true], 5); - - return redirect()->route('auth.totp')->with('authentication_token', $token); + if (!$user->use_totp) { + return $this->sendLoginResponse($user, $request); } - $this->auth->guard()->login($user, true); + Activity::event('auth:checkpoint')->withRequestMetadata()->subject($user)->log(); - return $this->sendLoginResponse($request); - } - - /** - * Handle a TOTP implementation page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View - */ - public function totp(Request $request) - { - $token = $request->session()->get('authentication_token'); - if (is_null($token) || $this->auth->guard()->user()) { - return redirect()->route('auth.login'); - } - - return view('auth.totp', ['verify_key' => $token]); - } - - /** - * Handle a login where the user is required to provide a TOTP authentication - * token. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response - * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException - * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException - * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException - */ - public function loginUsingTotp(Request $request) - { - if (is_null($request->input('verify_token'))) { - return $this->sendFailedLoginResponse($request); - } - - try { - $cache = $this->cache->pull($request->input('verify_token'), []); - $user = $this->repository->find(array_get($cache, 'user_id', 0)); - } catch (RecordNotFoundException $exception) { - return $this->sendFailedLoginResponse($request); - } - - if (is_null($request->input('2fa_token'))) { - return $this->sendFailedLoginResponse($request, $user); - } - - if (! $this->google2FA->verifyKey( - $this->encrypter->decrypt($user->totp_secret), - $request->input('2fa_token'), - $this->config->get('pterodactyl.auth.2fa.window') - )) { - return $this->sendFailedLoginResponse($request, $user); - } - - $this->auth->guard()->login($user, true); - - return $this->sendLoginResponse($request); - } - - /** - * Get the failed login response instance. - * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Contracts\Auth\Authenticatable|null $user - * @return \Illuminate\Http\RedirectResponse - */ - protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null): RedirectResponse - { - $this->incrementLoginAttempts($request); - $this->fireFailedLoginEvent($user, [ - $this->getField($request->input($this->username())) => $request->input($this->username()), + $request->session()->put('auth_confirmation_token', [ + 'user_id' => $user->id, + 'token_value' => $token = Str::random(64), + 'expires_at' => CarbonImmutable::now()->addMinutes(5), ]); - $errors = [$this->username() => trans('auth.failed')]; - - if ($request->expectsJson()) { - return response()->json($errors, 422); - } - - return redirect()->route('auth.login') - ->withInput($request->only($this->username())) - ->withErrors($errors); - } - - /** - * Get the login username to be used by the controller. - * - * @return string - */ - public function username() - { - return 'user'; - } - - /** - * Determine if the user is logging in using an email or username,. - * - * @param string $input - * @return string - */ - private function getField(string $input = null): string - { - return str_contains($input, '@') ? 'email' : 'username'; - } - - /** - * Fire a failed login event. - * - * @param \Illuminate\Contracts\Auth\Authenticatable|null $user - * @param array $credentials - */ - private function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = []) - { - event(new Failed(config('auth.defaults.guard'), $user, $credentials)); + return new JsonResponse([ + 'data' => [ + 'complete' => false, + 'confirmation_token' => $token, + ], + ]); } } diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index f13511d91..9b22f6f98 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -3,13 +3,16 @@ namespace Pterodactyl\Http\Controllers\Auth; use Illuminate\Support\Str; -use Illuminate\Http\Request; -use Prologue\Alerts\AlertsMessageBag; +use Pterodactyl\Models\User; +use Illuminate\Http\JsonResponse; use Illuminate\Contracts\Hashing\Hasher; +use Illuminate\Support\Facades\Password; use Illuminate\Auth\Events\PasswordReset; use Illuminate\Contracts\Events\Dispatcher; +use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ResetsPasswords; +use Pterodactyl\Http\Requests\Auth\ResetPasswordRequest; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; class ResetPasswordController extends Controller @@ -18,64 +21,46 @@ class ResetPasswordController extends Controller /** * The URL to redirect users to after password reset. - * - * @var string */ - public $redirectTo = '/'; + public string $redirectTo = '/'; - /** - * @var bool - */ - protected $hasTwoFactor = false; - - /** - * @var \Prologue\Alerts\AlertsMessageBag - */ - private $alerts; - - /** - * @var \Illuminate\Contracts\Events\Dispatcher - */ - private $dispatcher; - - /** - * @var \Illuminate\Contracts\Hashing\Hasher - */ - private $hasher; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $userRepository; + protected bool $hasTwoFactor = false; /** * ResetPasswordController constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alerts - * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher - * @param \Illuminate\Contracts\Hashing\Hasher $hasher - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository */ - public function __construct(AlertsMessageBag $alerts, Dispatcher $dispatcher, Hasher $hasher, UserRepositoryInterface $userRepository) - { - $this->alerts = $alerts; - $this->dispatcher = $dispatcher; - $this->hasher = $hasher; - $this->userRepository = $userRepository; + public function __construct( + private Dispatcher $dispatcher, + private Hasher $hasher, + private UserRepositoryInterface $userRepository + ) { } /** - * Return the rules used when validating password reset. + * Reset the given user's password. * - * @return array + * @throws \Pterodactyl\Exceptions\DisplayException */ - protected function rules(): array + public function __invoke(ResetPasswordRequest $request): JsonResponse { - return [ - 'token' => 'required', - 'email' => 'required|email', - 'password' => 'required|confirmed|min:8', - ]; + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise, we will parse the error and return the response. + $response = $this->broker()->reset( + $this->credentials($request), + function ($user, $password) { + $this->resetPassword($user, $password); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + if ($response === Password::PASSWORD_RESET) { + return $this->sendResetResponse(); + } + + throw new DisplayException(trans($response)); } /** @@ -83,13 +68,12 @@ class ResetPasswordController extends Controller * account do not automatically log them in. In those cases, send the user back to the login * form with a note telling them their password was changed and to log back in. * - * @param \Illuminate\Contracts\Auth\CanResetPassword|\Pterodactyl\Models\User $user - * @param string $password + * @param string $password * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - protected function resetPassword($user, $password) + protected function resetPassword(User $user, $password) { $user = $this->userRepository->update($user->id, [ 'password' => $this->hasher->make($password), @@ -100,7 +84,7 @@ class ResetPasswordController extends Controller // If the user is not using 2FA log them in, otherwise skip this step and force a // fresh login where they'll be prompted to enter a token. - if (! $user->use_totp) { + if (!$user->use_totp) { $this->guard()->login($user); } @@ -108,19 +92,14 @@ class ResetPasswordController extends Controller } /** - * Get the response for a successful password reset. - * - * @param \Illuminate\Http\Request $request - * @param string $response - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + * Send a successful password reset response back to the callee. */ - protected function sendResetResponse(Request $request, $response) + protected function sendResetResponse(): JsonResponse { - if ($this->hasTwoFactor) { - $this->alerts->success('Your password was successfully updated. Please log in to continue.')->flash(); - } - - return redirect($this->hasTwoFactor ? route('auth.login') : $this->redirectPath()) - ->with('status', trans($response)); + return response()->json([ + 'success' => true, + 'redirect_to' => $this->redirectTo, + 'send_to_login' => $this->hasTwoFactor, + ]); } } diff --git a/app/Http/Controllers/Base/AccountController.php b/app/Http/Controllers/Base/AccountController.php deleted file mode 100644 index 80811750e..000000000 --- a/app/Http/Controllers/Base/AccountController.php +++ /dev/null @@ -1,91 +0,0 @@ -alert = $alert; - $this->updateService = $updateService; - $this->sessionGuard = $authManager->guard(); - } - - /** - * Display base account information page. - * - * @return \Illuminate\View\View - */ - public function index() - { - return view('base.account', [ - 'languages' => $this->getAvailableLanguages(true), - ]); - } - - /** - * Update details for a user's account. - * - * @param \Pterodactyl\Http\Requests\Base\AccountDataFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(AccountDataFormRequest $request) - { - // Prevent logging this specific session out when the password is changed. This will - // automatically update the user's password anyways, so no need to do anything else here. - if ($request->input('do_action') === 'password') { - $this->sessionGuard->logoutOtherDevices($request->input('new_password')); - } else { - if ($request->input('do_action') === 'email') { - $data = ['email' => $request->input('new_email')]; - } elseif ($request->input('do_action') === 'identity') { - $data = $request->only(['name_first', 'name_last', 'username', 'language']); - } else { - $data = []; - } - - $this->updateService->setUserLevel(User::USER_LEVEL_USER); - $this->updateService->handle($request->user(), $data); - } - - $this->alert->success(trans('base.account.details_updated'))->flash(); - - return redirect()->route('account'); - } -} diff --git a/app/Http/Controllers/Base/AccountKeyController.php b/app/Http/Controllers/Base/AccountKeyController.php deleted file mode 100644 index 7161b4abf..000000000 --- a/app/Http/Controllers/Base/AccountKeyController.php +++ /dev/null @@ -1,115 +0,0 @@ -alert = $alert; - $this->keyService = $keyService; - $this->repository = $repository; - } - - /** - * Display a listing of all account API keys. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request): View - { - return view('base.api.index', [ - 'keys' => $this->repository->getAccountKeys($request->user()), - ]); - } - - /** - * Display account API key creation page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function create(Request $request): View - { - return view('base.api.new'); - } - - /** - * Handle saving new account API key. - * - * @param \Pterodactyl\Http\Requests\Base\StoreAccountKeyRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function store(StoreAccountKeyRequest $request) - { - if ($this->repository->findCountWhere(['user_id' => $request->user()->id]) >= 5) { - throw new DisplayException( - 'Cannot assign more than 5 API keys to an account.' - ); - } - - $this->keyService->setKeyType(ApiKey::TYPE_ACCOUNT)->handle([ - 'user_id' => $request->user()->id, - 'allowed_ips' => $request->input('allowed_ips'), - 'memo' => $request->input('memo'), - ]); - - $this->alert->success(trans('base.api.index.keypair_created'))->flash(); - - return redirect()->route('account.api'); - } - - /** - * Delete an account API key from the Panel via an AJAX request. - * - * @param \Illuminate\Http\Request $request - * @param string $identifier - * @return \Illuminate\Http\Response - */ - public function revoke(Request $request, string $identifier): Response - { - $this->repository->deleteAccountKey($request->user(), $identifier); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Base/ClientApiController.php b/app/Http/Controllers/Base/ClientApiController.php deleted file mode 100644 index a74c28db8..000000000 --- a/app/Http/Controllers/Base/ClientApiController.php +++ /dev/null @@ -1,109 +0,0 @@ -alert = $alert; - $this->creationService = $creationService; - $this->repository = $repository; - } - - /** - * Return all of the API keys available to this user. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request): View - { - return view('base.api.index', [ - 'keys' => $this->repository->getAccountKeys($request->user()), - ]); - } - - /** - * Render UI to allow creation of an API key. - * - * @return \Illuminate\View\View - */ - public function create(): View - { - return view('base.api.new'); - } - - /** - * Create the API key and return the user to the key listing page. - * - * @param \Pterodactyl\Http\Requests\Base\CreateClientApiKeyRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function store(CreateClientApiKeyRequest $request): RedirectResponse - { - $allowedIps = null; - if (! is_null($request->input('allowed_ips'))) { - $allowedIps = json_encode(explode(PHP_EOL, $request->input('allowed_ips'))); - } - - $this->creationService->setKeyType(ApiKey::TYPE_ACCOUNT)->handle([ - 'memo' => $request->input('memo'), - 'allowed_ips' => $allowedIps, - 'user_id' => $request->user()->id, - ]); - - $this->alert->success('A new client API key has been generated for your account.')->flash(); - - return redirect()->route('account.api'); - } - - /** - * Delete a client's API key from the panel. - * - * @param \Illuminate\Http\Request $request - * @param $identifier - * @return \Illuminate\Http\Response - */ - public function delete(Request $request, $identifier): Response - { - $this->repository->deleteAccountKey($request->user(), $identifier); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Base/IndexController.php b/app/Http/Controllers/Base/IndexController.php index 20ef370eb..ffa278b63 100644 --- a/app/Http/Controllers/Base/IndexController.php +++ b/app/Http/Controllers/Base/IndexController.php @@ -2,93 +2,27 @@ namespace Pterodactyl\Http\Controllers\Base; -use Illuminate\Http\Request; -use Pterodactyl\Models\User; -use Illuminate\Http\Response; -use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Exception\RequestException; +use Illuminate\View\View; +use Illuminate\View\Factory as ViewFactory; use Pterodactyl\Http\Controllers\Controller; -use Symfony\Component\HttpKernel\Exception\HttpException; -use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class IndexController extends Controller { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonRepository; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService - */ - protected $keyProviderService; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $repository; - /** * IndexController constructor. - * - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ public function __construct( - DaemonKeyProviderService $keyProviderService, - DaemonServerRepositoryInterface $daemonRepository, - ServerRepositoryInterface $repository + protected ServerRepositoryInterface $repository, + protected ViewFactory $view ) { - $this->daemonRepository = $daemonRepository; - $this->keyProviderService = $keyProviderService; - $this->repository = $repository; } /** * Returns listing of user's servers. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View */ - public function getIndex(Request $request) + public function index(): View { - $servers = $this->repository->setSearchTerm($request->input('query'))->filterUserAccessServers( - $request->user(), User::FILTER_LEVEL_ALL, config('pterodactyl.paginate.frontend.servers') - ); - - return view('base.index', ['servers' => $servers]); - } - - /** - * Returns status of the server in a JSON response used for populating active status list. - * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @return \Illuminate\Http\JsonResponse - * @throws \Exception - */ - public function status(Request $request, $uuid) - { - $server = $this->repository->findFirstWhere([['uuidShort', '=', $uuid]]); - $token = $this->keyProviderService->handle($server, $request->user()); - - if (! $server->installed) { - return response()->json(['status' => 20]); - } elseif ($server->suspended) { - return response()->json(['status' => 30]); - } - - try { - $response = $this->daemonRepository->setServer($server)->setToken($token)->details(); - } catch (ConnectException $exception) { - throw new HttpException(Response::HTTP_GATEWAY_TIMEOUT, $exception->getMessage()); - } catch (RequestException $exception) { - throw new HttpException(500, $exception->getMessage()); - } - - return response()->json(json_decode($response->getBody())); + return view('templates/base.core'); } } diff --git a/app/Http/Controllers/Base/LocaleController.php b/app/Http/Controllers/Base/LocaleController.php new file mode 100644 index 000000000..5214defdd --- /dev/null +++ b/app/Http/Controllers/Base/LocaleController.php @@ -0,0 +1,73 @@ +loader = $translator->getLoader(); + } + + /** + * Returns translation data given a specific locale and namespace. + */ + public function __invoke(Request $request): JsonResponse + { + $locales = explode(' ', $request->input('locale') ?? ''); + $namespaces = explode(' ', $request->input('namespace') ?? ''); + + $response = []; + foreach ($locales as $locale) { + $response[$locale] = []; + foreach ($namespaces as $namespace) { + $response[$locale][$namespace] = $this->i18n( + $this->loader->load($locale, str_replace('.', '/', $namespace)) + ); + } + } + + return new JsonResponse($response, 200, [ + // Cache this in the browser for an hour, and allow the browser to use a stale + // cache for up to a day after it was created while it fetches an updated set + // of translation keys. + 'Cache-Control' => 'public, max-age=3600, stale-while-revalidate=86400', + 'ETag' => md5(json_encode($response, JSON_THROW_ON_ERROR)), + ]); + } + + /** + * Convert standard Laravel translation keys that look like ":foo" + * into key structures that are supported by the front-end i18n + * library, like "{{foo}}". + */ + protected function i18n(array $data): array + { + foreach ($data as $key => $value) { + if (is_array($value)) { + $data[$key] = $this->i18n($value); + } else { + // Find a Laravel style translation replacement in the string and replace it with + // one that the front-end is able to use. This won't always be present, especially + // for complex strings or things where we'd never have a backend component anyways. + // + // For example: + // "Hello :name, the :notifications.0.title notification needs :count actions :foo.0.bar." + // + // Becomes: + // "Hello {{name}}, the {{notifications.0.title}} notification needs {{count}} actions {{foo.0.bar}}." + $data[$key] = preg_replace('/:([\w.-]+\w)([^\w:]?|$)/m', '{{$1}}$2', $value); + } + } + + return $data; + } +} diff --git a/app/Http/Controllers/Base/SecurityController.php b/app/Http/Controllers/Base/SecurityController.php deleted file mode 100644 index 2aa9ac129..000000000 --- a/app/Http/Controllers/Base/SecurityController.php +++ /dev/null @@ -1,153 +0,0 @@ -alert = $alert; - $this->config = $config; - $this->repository = $repository; - $this->toggleTwoFactorService = $toggleTwoFactorService; - $this->twoFactorSetupService = $twoFactorSetupService; - } - - /** - * Returns Security Management Page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request) - { - if ($this->config->get('session.driver') === 'database') { - $activeSessions = $this->repository->getUserSessions($request->user()->id); - } - - return view('base.security', [ - 'sessions' => $activeSessions ?? null, - ]); - } - - /** - * Generates TOTP Secret and returns popup data for user to verify - * that they can generate a valid response. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function generateTotp(Request $request) - { - $totpData = $this->twoFactorSetupService->handle($request->user()); - - return response()->json([ - 'qrImage' => 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' . $totpData, - ]); - } - - /** - * Verifies that 2FA token received is valid and will work on the account. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function setTotp(Request $request) - { - try { - $this->toggleTwoFactorService->handle($request->user(), $request->input('token') ?? ''); - - return response('true'); - } catch (TwoFactorAuthenticationTokenInvalid $exception) { - return response('false'); - } - } - - /** - * Disables TOTP on an account. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function disableTotp(Request $request) - { - try { - $this->toggleTwoFactorService->handle($request->user(), $request->input('token') ?? '', false); - } catch (TwoFactorAuthenticationTokenInvalid $exception) { - $this->alert->danger(trans('base.security.2fa_disable_error'))->flash(); - } - - return redirect()->route('account.security'); - } - - /** - * Revokes a user session. - * - * @param \Illuminate\Http\Request $request - * @param string $id - * @return \Illuminate\Http\RedirectResponse - */ - public function revoke(Request $request, string $id) - { - $this->repository->deleteUserSession($request->user()->id, $id); - - return redirect()->route('account.security'); - } -} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 2581e8b98..ee7af2f7a 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -9,5 +9,7 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests; abstract class Controller extends BaseController { - use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + use AuthorizesRequests; + use DispatchesJobs; + use ValidatesRequests; } diff --git a/app/Http/Controllers/Daemon/ActionController.php b/app/Http/Controllers/Daemon/ActionController.php deleted file mode 100644 index 257d8775a..000000000 --- a/app/Http/Controllers/Daemon/ActionController.php +++ /dev/null @@ -1,107 +0,0 @@ -eventDispatcher = $eventDispatcher; - $this->repository = $repository; - } - - /** - * Handles install toggle request from daemon. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function markInstall(Request $request): JsonResponse - { - try { - /** @var \Pterodactyl\Models\Server $server */ - $server = $this->repository->findFirstWhere([ - 'uuid' => $request->input('server'), - ]); - } catch (RecordNotFoundException $exception) { - return JsonResponse::create([ - 'error' => 'No server by that ID was found on the system.', - ], Response::HTTP_UNPROCESSABLE_ENTITY); - } - - if (! $server->relationLoaded('node')) { - $server->load('node'); - } - - $hmac = $request->input('signed'); - $status = $request->input('installed'); - - if (! hash_equals(base64_decode($hmac), hash_hmac('sha256', $server->uuid, $server->getRelation('node')->daemonSecret, true))) { - return JsonResponse::create([ - 'error' => 'Signed HMAC was invalid.', - ], Response::HTTP_FORBIDDEN); - } - - $this->repository->update($server->id, [ - 'installed' => ($status === 'installed') ? 1 : 2, - ], true, true); - - // Only fire event if server installed successfully. - if ($status === 'installed') { - $this->eventDispatcher->dispatch(new ServerInstalled($server)); - } - - // Don't use a 204 here, the daemon is hard-checking for a 200 code. - return JsonResponse::create([]); - } - - /** - * Handles configuration data request from daemon. - * - * @param \Illuminate\Http\Request $request - * @param string $token - * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response - */ - public function configuration(Request $request, $token) - { - $nodeId = Cache::pull('Node:Configuration:' . $token); - if (is_null($nodeId)) { - return response()->json(['error' => 'token_invalid'], 403); - } - - $node = Node::findOrFail($nodeId); - - // Manually as getConfigurationAsJson() returns it in correct format already - return response($node->getConfigurationAsJson())->header('Content-Type', 'text/json'); - } -} diff --git a/app/Http/Controllers/Daemon/PackController.php b/app/Http/Controllers/Daemon/PackController.php deleted file mode 100644 index ce482013f..000000000 --- a/app/Http/Controllers/Daemon/PackController.php +++ /dev/null @@ -1,73 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Daemon; - -use Storage; -use Pterodactyl\Models; -use Illuminate\Http\Request; -use Pterodactyl\Http\Controllers\Controller; - -class PackController extends Controller -{ - /** - * Pulls an install pack archive from the system. - * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse - */ - public function pull(Request $request, $uuid) - { - $pack = Models\Pack::where('uuid', $uuid)->first(); - - if (! $pack) { - return response()->json(['error' => 'No such pack.'], 404); - } - - if (! Storage::exists('packs/' . $pack->uuid . '/archive.tar.gz')) { - return response()->json(['error' => 'There is no archive available for this pack.'], 503); - } - - return response()->download(storage_path('app/packs/' . $pack->uuid . '/archive.tar.gz')); - } - - /** - * Returns the hash information for a pack. - * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @return \Illuminate\Http\JsonResponse - */ - public function hash(Request $request, $uuid) - { - $pack = Models\Pack::where('uuid', $uuid)->first(); - - if (! $pack) { - return response()->json(['error' => 'No such pack.'], 404); - } - - if (! Storage::exists('packs/' . $pack->uuid . '/archive.tar.gz')) { - return response()->json(['error' => 'There is no archive available for this pack.'], 503); - } - - return response()->json([ - 'archive.tar.gz' => sha1_file(storage_path('app/packs/' . $pack->uuid . '/archive.tar.gz')), - ]); - } - - /** - * Pulls an update pack archive from the system. - * - * @param \Illuminate\Http\Request $request - */ - public function pullUpdate(Request $request) - { - } -} diff --git a/app/Http/Controllers/Server/ConsoleController.php b/app/Http/Controllers/Server/ConsoleController.php deleted file mode 100644 index 0c1959251..000000000 --- a/app/Http/Controllers/Server/ConsoleController.php +++ /dev/null @@ -1,72 +0,0 @@ -config = $config; - } - - /** - * Render server index page with the console and power options. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - - $this->setRequest($request)->injectJavascript([ - 'server' => [ - 'cpu' => $server->cpu, - ], - 'meta' => [ - 'saveFile' => route('server.files.save', $server->uuidShort), - 'csrfToken' => csrf_token(), - ], - 'config' => [ - 'console_count' => $this->config->get('pterodactyl.console.count'), - 'console_freq' => $this->config->get('pterodactyl.console.frequency'), - ], - ]); - - return view('server.index'); - } - - /** - * Render a stand-alone console in the browser. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - */ - public function console(Request $request): View - { - $this->setRequest($request)->injectJavascript(['config' => [ - 'console_count' => $this->config->get('pterodactyl.console.count'), - 'console_freq' => $this->config->get('pterodactyl.console.frequency'), - ]]); - - return view('server.console'); - } -} diff --git a/app/Http/Controllers/Server/DatabaseController.php b/app/Http/Controllers/Server/DatabaseController.php deleted file mode 100644 index d897ca0cb..000000000 --- a/app/Http/Controllers/Server/DatabaseController.php +++ /dev/null @@ -1,163 +0,0 @@ -alert = $alert; - $this->databaseHostRepository = $databaseHostRepository; - $this->deployServerDatabaseService = $deployServerDatabaseService; - $this->managementService = $managementService; - $this->passwordService = $passwordService; - $this->repository = $repository; - } - - /** - * Render the database listing for a server. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('view-databases', $server); - $this->setRequest($request)->injectJavascript(); - - $canCreateDatabase = config('pterodactyl.client_features.databases.enabled'); - $allowRandom = config('pterodactyl.client_features.databases.allow_random'); - - if ($this->databaseHostRepository->findCountWhere([['node_id', '=', $server->node_id]]) === 0) { - if ($canCreateDatabase && ! $allowRandom) { - $canCreateDatabase = false; - } - } - - $databases = $this->repository->getDatabasesForServer($server->id); - - return view('server.databases.index', [ - 'allowCreation' => $canCreateDatabase, - 'overLimit' => ! is_null($server->database_limit) && count($databases) >= $server->database_limit, - 'databases' => $databases, - ]); - } - - /** - * Handle a request from a user to create a new database for the server. - * - * @param \Pterodactyl\Http\Requests\Server\Database\StoreServerDatabaseRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException - */ - public function store(StoreServerDatabaseRequest $request): RedirectResponse - { - $this->deployServerDatabaseService->handle($request->getServer(), $request->validated()); - - $this->alert->success('Successfully created a new database.')->flash(); - - return redirect()->route('server.databases.index', $request->getServer()->uuidShort); - } - - /** - * Handle a request to update the password for a specific database. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Throwable - */ - public function update(Request $request): JsonResponse - { - $this->authorize('reset-db-password', $request->attributes->get('server')); - - $password = $this->passwordService->handle($request->attributes->get('database')); - - return response()->json(['password' => $password]); - } - - /** - * Delete a database for this server from the SQL server and Panel database. - * - * @param \Pterodactyl\Http\Requests\Server\Database\DeleteServerDatabaseRequest $request - * @return \Illuminate\Http\Response - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function delete(DeleteServerDatabaseRequest $request): Response - { - $this->managementService->delete($request->attributes->get('database')->id); - - return response('', Response::HTTP_NO_CONTENT); - } -} diff --git a/app/Http/Controllers/Server/Files/DownloadController.php b/app/Http/Controllers/Server/Files/DownloadController.php deleted file mode 100644 index 04b16d084..000000000 --- a/app/Http/Controllers/Server/Files/DownloadController.php +++ /dev/null @@ -1,57 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Server\Files; - -use Ramsey\Uuid\Uuid; -use Illuminate\Http\Request; -use Illuminate\Cache\Repository; -use Illuminate\Http\RedirectResponse; -use Pterodactyl\Http\Controllers\Controller; - -class DownloadController extends Controller -{ - /** - * @var \Illuminate\Cache\Repository - */ - protected $cache; - - /** - * DownloadController constructor. - * - * @param \Illuminate\Cache\Repository $cache - */ - public function __construct(Repository $cache) - { - $this->cache = $cache; - } - - /** - * Setup a unique download link for a user to download a file from. - * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @param string $file - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request, string $uuid, string $file): RedirectResponse - { - $server = $request->attributes->get('server'); - $this->authorize('download-files', $server); - - $token = Uuid::uuid4()->toString(); - $node = $server->getRelation('node'); - - $this->cache->put('Server:Downloads:' . $token, ['server' => $server->uuid, 'path' => $file], 5); - - return redirect(sprintf('%s://%s:%s/v1/server/file/download/%s', $node->scheme, $node->fqdn, $node->daemonListen, $token)); - } -} diff --git a/app/Http/Controllers/Server/Files/FileActionsController.php b/app/Http/Controllers/Server/Files/FileActionsController.php deleted file mode 100644 index bd63009a6..000000000 --- a/app/Http/Controllers/Server/Files/FileActionsController.php +++ /dev/null @@ -1,120 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Controllers\Server\Files; - -use Illuminate\View\View; -use Illuminate\Http\Request; -use GuzzleHttp\Exception\RequestException; -use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Traits\Controllers\JavascriptInjection; -use Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; - -class FileActionsController extends Controller -{ - use JavascriptInjection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface - */ - protected $repository; - - /** - * FileActionsController constructor. - * - * @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $repository - */ - public function __construct(FileRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * Display server file index list. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('list-files', $server); - - $this->setRequest($request)->injectJavascript([ - 'meta' => [ - 'directoryList' => route('server.files.directory-list', $server->uuidShort), - 'csrftoken' => csrf_token(), - ], - 'permissions' => [ - 'moveFiles' => $request->user()->can('move-files', $server), - 'copyFiles' => $request->user()->can('copy-files', $server), - 'compressFiles' => $request->user()->can('compress-files', $server), - 'decompressFiles' => $request->user()->can('decompress-files', $server), - 'createFiles' => $request->user()->can('create-files', $server), - 'downloadFiles' => $request->user()->can('download-files', $server), - 'deleteFiles' => $request->user()->can('delete-files', $server), - ], - ]); - - return view('server.files.index'); - } - - /** - * Render page to manually create a file in the panel. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function create(Request $request): View - { - $this->authorize('create-files', $request->attributes->get('server')); - $this->setRequest($request)->injectJavascript(); - - return view('server.files.add', [ - 'directory' => (in_array($request->get('dir'), [null, '/', ''])) ? '' : trim($request->get('dir'), '/') . '/', - ]); - } - - /** - * Display a form to allow for editing of a file. - * - * @param \Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest $request - * @param string $uuid - * @param string $file - * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - */ - public function view(UpdateFileContentsFormRequest $request, string $uuid, string $file): View - { - $server = $request->attributes->get('server'); - - $dirname = str_replace('\\', '/', pathinfo($file, PATHINFO_DIRNAME)); - try { - $content = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getContent($file); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } - - $this->setRequest($request)->injectJavascript(['stat' => $request->attributes->get('file_stats')]); - - return view('server.files.edit', [ - 'file' => $file, - 'stat' => $request->attributes->get('file_stats'), - 'contents' => $content, - 'directory' => (in_array($dirname, ['.', './', '/'])) ? '/' : trim($dirname, '/') . '/', - ]); - } -} diff --git a/app/Http/Controllers/Server/Files/RemoteRequestController.php b/app/Http/Controllers/Server/Files/RemoteRequestController.php deleted file mode 100644 index ab58037d0..000000000 --- a/app/Http/Controllers/Server/Files/RemoteRequestController.php +++ /dev/null @@ -1,105 +0,0 @@ -config = $config; - $this->repository = $repository; - } - - /** - * Return a listing of a servers file directory. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function directory(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('list-files', $server); - - $requestDirectory = '/' . trim(urldecode($request->input('directory', '/')), '/'); - $directory = [ - 'header' => $requestDirectory !== '/' ? $requestDirectory : '', - 'first' => $requestDirectory !== '/', - ]; - - $goBack = explode('/', trim($requestDirectory, '/')); - if (! empty(array_filter($goBack)) && count($goBack) >= 2) { - array_pop($goBack); - - $directory['show'] = true; - $directory['link'] = '/' . implode('/', $goBack); - $directory['link_show'] = implode('/', $goBack) . '/'; - } - - try { - $listing = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getDirectory($requestDirectory); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception, true); - } - - return view('server.files.list', [ - 'files' => $listing['files'], - 'folders' => $listing['folders'], - 'editableMime' => $this->config->get('pterodactyl.files.editable'), - 'directory' => $directory, - ]); - } - - /** - * Put the contents of a file onto the daemon. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - */ - public function store(Request $request): Response - { - $server = $request->attributes->get('server'); - $this->authorize('save-files', $server); - - try { - $this->repository->setServer($server)->setToken($request->attributes->get('server_token')) - ->putContent($request->input('file'), $request->input('contents') ?? ''); - - return response('', 204); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } - } -} diff --git a/app/Http/Controllers/Server/Settings/AllocationController.php b/app/Http/Controllers/Server/Settings/AllocationController.php deleted file mode 100644 index 21baf7c0d..000000000 --- a/app/Http/Controllers/Server/Settings/AllocationController.php +++ /dev/null @@ -1,96 +0,0 @@ -defaultAllocationService = $defaultAllocationService; - $this->hashids = $hashids; - $this->repository = $repository; - } - - /** - * Render the allocation management overview page for a server. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('view-allocations', $server); - $this->setRequest($request)->injectJavascript(); - - return view('server.settings.allocation', [ - 'allocations' => $this->repository->findWhere([['server_id', '=', $server->id]]), - ]); - } - - /** - * Update the default allocation for a server. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\JsonResponse - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(Request $request): JsonResponse - { - $server = $request->attributes->get('server'); - $this->authorize('edit-allocation', $server); - - $allocation = $this->hashids->decodeFirst($request->input('allocation'), 0); - - try { - $this->defaultAllocationService->handle($server->id, $allocation); - } catch (AllocationDoesNotBelongToServerException $exception) { - return response()->json(['error' => 'No matching allocation was located for this server.'], 404); - } - - return response()->json(); - } -} diff --git a/app/Http/Controllers/Server/Settings/NameController.php b/app/Http/Controllers/Server/Settings/NameController.php deleted file mode 100644 index 29cdb9ed9..000000000 --- a/app/Http/Controllers/Server/Settings/NameController.php +++ /dev/null @@ -1,59 +0,0 @@ -repository = $repository; - } - - /** - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request) - { - $this->authorize('view-name', $request->attributes->get('server')); - $this->setRequest($request)->injectJavascript(); - - return view('server.settings.name'); - } - - /** - * Update the stored name for a specific server. - * - * @param \Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(ChangeServerNameRequest $request): RedirectResponse - { - $this->repository->update($request->getServer()->id, $request->validated()); - - return redirect()->route('server.settings.name', $request->getServer()->uuidShort); - } -} diff --git a/app/Http/Controllers/Server/Settings/SftpController.php b/app/Http/Controllers/Server/Settings/SftpController.php deleted file mode 100644 index 58b110bd3..000000000 --- a/app/Http/Controllers/Server/Settings/SftpController.php +++ /dev/null @@ -1,29 +0,0 @@ -authorize('access-sftp', $request->attributes->get('server')); - $this->setRequest($request)->injectJavascript(); - - return view('server.settings.sftp'); - } -} diff --git a/app/Http/Controllers/Server/Settings/StartupController.php b/app/Http/Controllers/Server/Settings/StartupController.php deleted file mode 100644 index 8f17022b5..000000000 --- a/app/Http/Controllers/Server/Settings/StartupController.php +++ /dev/null @@ -1,96 +0,0 @@ -alert = $alert; - $this->commandViewService = $commandViewService; - $this->modificationService = $modificationService; - } - - /** - * Render the server startup page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('view-startup', $server); - $this->setRequest($request)->injectJavascript(); - - $data = $this->commandViewService->handle($server->id); - - return view('server.settings.startup', [ - 'variables' => $data->get('variables'), - 'server_values' => $data->get('server_values'), - 'startup' => $data->get('startup'), - ]); - } - - /** - * Handle request to update the startup variables for a server. Authorization - * is handled in the form request. - * - * @param \Pterodactyl\Http\Requests\Server\UpdateStartupParametersFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(UpdateStartupParametersFormRequest $request): RedirectResponse - { - $this->modificationService->setUserLevel(User::USER_LEVEL_USER); - $this->modificationService->handle($request->attributes->get('server'), $request->normalize()); - $this->alert->success(trans('server.config.startup.edited'))->flash(); - - return redirect()->route('server.settings.startup', ['server' => $request->attributes->get('server')->uuidShort]); - } -} diff --git a/app/Http/Controllers/Server/SubuserController.php b/app/Http/Controllers/Server/SubuserController.php deleted file mode 100644 index 008cfeaa5..000000000 --- a/app/Http/Controllers/Server/SubuserController.php +++ /dev/null @@ -1,197 +0,0 @@ -alert = $alert; - $this->repository = $repository; - $this->subuserCreationService = $subuserCreationService; - $this->subuserDeletionService = $subuserDeletionService; - $this->subuserUpdateService = $subuserUpdateService; - } - - /** - * Displays the subuser overview index. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('list-subusers', $server); - $this->setRequest($request)->injectJavascript(); - - return view('server.users.index', [ - 'subusers' => $this->repository->findWhere([['server_id', '=', $server->id]]), - ]); - } - - /** - * Displays a single subuser overview. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function view(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('view-subuser', $server); - - $subuser = $this->repository->getWithPermissions($request->attributes->get('subuser')); - $this->setRequest($request)->injectJavascript(); - - return view('server.users.view', [ - 'subuser' => $subuser, - 'permlist' => Permission::getPermissions(), - 'permissions' => $subuser->getRelation('permissions')->mapWithKeys(function ($item) { - return [$item->permission => true]; - }), - ]); - } - - /** - * Handles editing a subuser. - * - * @param \Pterodactyl\Http\Requests\Server\Subuser\SubuserUpdateFormRequest $request - * @param string $uuid - * @param string $hash - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update(SubuserUpdateFormRequest $request, string $uuid, string $hash): RedirectResponse - { - $this->subuserUpdateService->handle($request->attributes->get('subuser'), $request->input('permissions', [])); - $this->alert->success(trans('server.users.user_updated'))->flash(); - - return redirect()->route('server.subusers.view', ['uuid' => $uuid, 'subuser' => $hash]); - } - - /** - * Display new subuser creation page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function create(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('create-subuser', $server); - $this->setRequest($request)->injectJavascript(); - - return view('server.users.new', ['permissions' => Permission::getPermissions()]); - } - - /** - * Handles creating a new subuser. - * - * @param \Pterodactyl\Http\Requests\Server\Subuser\SubuserStoreFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException - * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException - */ - public function store(SubuserStoreFormRequest $request): RedirectResponse - { - $server = $request->attributes->get('server'); - - $subuser = $this->subuserCreationService->handle($server, $request->input('email'), $request->input('permissions', [])); - $this->alert->success(trans('server.users.user_assigned'))->flash(); - - return redirect()->route('server.subusers.view', [ - 'uuid' => $server->uuidShort, - 'id' => $subuser->hashid, - ]); - } - - /** - * Handles deleting a subuser. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function delete(Request $request): Response - { - $server = $request->attributes->get('server'); - $this->authorize('delete-subuser', $server); - - $this->subuserDeletionService->handle($request->attributes->get('subuser')); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Server/Tasks/ActionController.php b/app/Http/Controllers/Server/Tasks/ActionController.php deleted file mode 100644 index 498db8537..000000000 --- a/app/Http/Controllers/Server/Tasks/ActionController.php +++ /dev/null @@ -1,79 +0,0 @@ -processScheduleService = $processScheduleService; - $this->repository = $repository; - } - - /** - * Toggle a task to be active or inactive for a given server. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function toggle(Request $request): Response - { - $server = $request->attributes->get('server'); - $schedule = $request->attributes->get('schedule'); - $this->authorize('toggle-schedule', $server); - - $this->repository->update($schedule->id, [ - 'is_active' => ! $schedule->is_active, - ]); - - return response('', 204); - } - - /** - * Trigger a schedule to run now. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * - * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function trigger(Request $request): Response - { - $server = $request->attributes->get('server'); - $this->authorize('toggle-schedule', $server); - - $this->processScheduleService->handle( - $request->attributes->get('schedule') - ); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Server/Tasks/TaskManagementController.php b/app/Http/Controllers/Server/Tasks/TaskManagementController.php deleted file mode 100644 index 9805b5cff..000000000 --- a/app/Http/Controllers/Server/Tasks/TaskManagementController.php +++ /dev/null @@ -1,198 +0,0 @@ -alert = $alert; - $this->creationService = $creationService; - $this->hashids = $hashids; - $this->repository = $repository; - $this->updateService = $updateService; - } - - /** - * Display the task page listing. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function index(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('list-schedules', $server); - $this->setRequest($request)->injectJavascript(); - - return view('server.schedules.index', [ - 'schedules' => $this->repository->findServerSchedules($server->id), - 'actions' => [ - 'command' => trans('server.schedule.actions.command'), - 'power' => trans('server.schedule.actions.power'), - ], - ]); - } - - /** - * Display the task creation page. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function create(Request $request): View - { - $server = $request->attributes->get('server'); - $this->authorize('create-schedule', $server); - $this->setRequest($request)->injectJavascript(); - - return view('server.schedules.new'); - } - - /** - * Handle request to store a new schedule and tasks in the database. - * - * @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException - */ - public function store(ScheduleCreationFormRequest $request): RedirectResponse - { - $server = $request->attributes->get('server'); - - $schedule = $this->creationService->handle($server, $request->normalize(), $request->getTasks()); - $this->alert->success(trans('server.schedule.schedule_created'))->flash(); - - return redirect()->route('server.schedules.view', [ - 'server' => $server->uuidShort, - 'schedule' => $schedule->hashid, - ]); - } - - /** - * Return a view to modify a schedule. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\View\View - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function view(Request $request): View - { - $server = $request->attributes->get('server'); - $schedule = $request->attributes->get('schedule'); - $this->authorize('view-schedule', $server); - - $this->setRequest($request)->injectJavascript([ - 'tasks' => $schedule->getRelation('tasks')->map(function ($task) { - /* @var \Pterodactyl\Models\Task $task */ - return collect($task->toArray())->only('action', 'time_offset', 'payload')->all(); - }), - ]); - - return view('server.schedules.view', ['schedule' => $schedule]); - } - - /** - * Update a specific parent task on the system. - * - * @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request - * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException - */ - public function update(ScheduleCreationFormRequest $request): RedirectResponse - { - $server = $request->attributes->get('server'); - $schedule = $request->attributes->get('schedule'); - - $this->updateService->handle($schedule, $request->normalize(), $request->getTasks()); - $this->alert->success(trans('server.schedule.schedule_updated'))->flash(); - - return redirect()->route('server.schedules.view', [ - 'server' => $server->uuidShort, - 'schedule' => $schedule->hashid, - ]); - } - - /** - * Delete a parent task from the Panel. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - public function delete(Request $request): Response - { - $server = $request->attributes->get('server'); - $schedule = $request->attributes->get('schedule'); - $this->authorize('delete-schedule', $server); - - $this->repository->delete($schedule->id); - - return response('', 204); - } -} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 85c1190ea..b7085d9ed 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,90 +2,80 @@ namespace Pterodactyl\Http; -use Pterodactyl\Models\ApiKey; use Illuminate\Auth\Middleware\Authorize; +use Illuminate\Http\Middleware\HandleCors; use Illuminate\Auth\Middleware\Authenticate; +use Illuminate\Http\Middleware\TrustProxies; use Pterodactyl\Http\Middleware\TrimStrings; -use Pterodactyl\Http\Middleware\TrustProxies; use Illuminate\Session\Middleware\StartSession; use Pterodactyl\Http\Middleware\EncryptCookies; +use Pterodactyl\Http\Middleware\Api\IsValidJson; use Pterodactyl\Http\Middleware\VerifyCsrfToken; use Pterodactyl\Http\Middleware\VerifyReCaptcha; -use Pterodactyl\Http\Middleware\AdminAuthenticate; use Illuminate\Routing\Middleware\ThrottleRequests; use Pterodactyl\Http\Middleware\LanguageMiddleware; use Illuminate\Foundation\Http\Kernel as HttpKernel; -use Pterodactyl\Http\Middleware\Api\AuthenticateKey; use Illuminate\Routing\Middleware\SubstituteBindings; -use Pterodactyl\Http\Middleware\Api\SetSessionDriver; +use Pterodactyl\Http\Middleware\Activity\TrackAPIKey; use Illuminate\Session\Middleware\AuthenticateSession; use Illuminate\View\Middleware\ShareErrorsFromSession; use Pterodactyl\Http\Middleware\MaintenanceMiddleware; +use Pterodactyl\Http\Middleware\EnsureStatefulRequests; use Pterodactyl\Http\Middleware\RedirectIfAuthenticated; use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth; use Pterodactyl\Http\Middleware\Api\AuthenticateIPAccess; -use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings; use Illuminate\Foundation\Http\Middleware\ValidatePostSize; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; -use Pterodactyl\Http\Middleware\Server\AccessingValidServer; -use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser; use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate; -use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer; +use Pterodactyl\Http\Middleware\Api\Client\RequireClientApiKey; use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication; -use Pterodactyl\Http\Middleware\Server\DatabaseBelongsToServer; -use Pterodactyl\Http\Middleware\Server\ScheduleBelongsToServer; -use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode; use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; -use Pterodactyl\Http\Middleware\Api\Client\SubstituteClientApiBindings; +use Pterodactyl\Http\Middleware\Api\Client\SubstituteClientBindings; +use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance; use Pterodactyl\Http\Middleware\Api\Application\AuthenticateApplicationUser; -use Pterodactyl\Http\Middleware\DaemonAuthenticate as OldDaemonAuthenticate; class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. - * - * @var array */ protected $middleware = [ - CheckForMaintenanceMode::class, + TrustProxies::class, + HandleCors::class, + PreventRequestsDuringMaintenance::class, ValidatePostSize::class, TrimStrings::class, ConvertEmptyStringsToNull::class, - TrustProxies::class, ]; /** * The application's route middleware groups. - * - * @var array */ protected $middlewareGroups = [ 'web' => [ EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, - AuthenticateSession::class, ShareErrorsFromSession::class, VerifyCsrfToken::class, SubstituteBindings::class, LanguageMiddleware::class, - RequireTwoFactorAuthentication::class, ], 'api' => [ - 'throttle:120,1', - ApiSubstituteBindings::class, - SetSessionDriver::class, - 'api..key:' . ApiKey::TYPE_APPLICATION, - AuthenticateApplicationUser::class, + EnsureStatefulRequests::class, + 'auth:sanctum', + IsValidJson::class, + TrackAPIKey::class, + RequireTwoFactorAuthentication::class, AuthenticateIPAccess::class, ], + 'application-api' => [ + SubstituteBindings::class, + AuthenticateApplicationUser::class, + ], 'client-api' => [ - 'throttle:60,1', - SubstituteClientApiBindings::class, - SetSessionDriver::class, - 'api..key:' . ApiKey::TYPE_ACCOUNT, - AuthenticateIPAccess::class, + SubstituteClientBindings::class, + RequireClientApiKey::class, ], 'daemon' => [ SubstituteBindings::class, @@ -95,33 +85,17 @@ class Kernel extends HttpKernel /** * The application's route middleware. - * - * @var array */ - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'auth' => Authenticate::class, 'auth.basic' => AuthenticateWithBasicAuth::class, + 'auth.session' => AuthenticateSession::class, 'guest' => RedirectIfAuthenticated::class, - 'server' => AccessingValidServer::class, - 'subuser.auth' => AuthenticateAsSubuser::class, - 'admin' => AdminAuthenticate::class, - 'daemon-old' => OldDaemonAuthenticate::class, 'csrf' => VerifyCsrfToken::class, 'throttle' => ThrottleRequests::class, 'can' => Authorize::class, 'bindings' => SubstituteBindings::class, 'recaptcha' => VerifyReCaptcha::class, 'node.maintenance' => MaintenanceMiddleware::class, - - // Server specific middleware (used for authenticating access to resources) - // - // These are only used for individual server authentication, and not global - // actions from other resources. They are defined in the route files. - 'server..database' => DatabaseBelongsToServer::class, - 'server..subuser' => SubuserBelongsToServer::class, - 'server..schedule' => ScheduleBelongsToServer::class, - - // API Specific Middleware - 'api..key' => AuthenticateKey::class, ]; } diff --git a/app/Http/Middleware/Activity/AccountSubject.php b/app/Http/Middleware/Activity/AccountSubject.php new file mode 100644 index 000000000..fba1ea087 --- /dev/null +++ b/app/Http/Middleware/Activity/AccountSubject.php @@ -0,0 +1,21 @@ +user()); + LogTarget::setSubject($request->user()); + + return $next($request); + } +} diff --git a/app/Http/Middleware/Activity/ServerSubject.php b/app/Http/Middleware/Activity/ServerSubject.php new file mode 100644 index 000000000..c4dd160c8 --- /dev/null +++ b/app/Http/Middleware/Activity/ServerSubject.php @@ -0,0 +1,29 @@ +route()->parameter('server'); + if ($server instanceof Server) { + LogTarget::setActor($request->user()); + LogTarget::setSubject($server); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Activity/TrackAPIKey.php b/app/Http/Middleware/Activity/TrackAPIKey.php new file mode 100644 index 000000000..78aa7055f --- /dev/null +++ b/app/Http/Middleware/Activity/TrackAPIKey.php @@ -0,0 +1,27 @@ +user()) { + $token = $request->user()->currentAccessToken(); + + LogTarget::setApiKeyId($token instanceof ApiKey ? $token->id : null); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/AdminAuthenticate.php b/app/Http/Middleware/AdminAuthenticate.php index 6307669c3..4c7f48bef 100644 --- a/app/Http/Middleware/AdminAuthenticate.php +++ b/app/Http/Middleware/AdminAuthenticate.php @@ -1,15 +1,7 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Http\Middleware; -use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -18,16 +10,12 @@ class AdminAuthenticate /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { - if (! $request->user() || ! $request->user()->root_admin) { - throw new AccessDeniedHttpException; + if (!$request->user() || !$request->user()->root_admin) { + throw new AccessDeniedHttpException(); } return $next($request); diff --git a/app/Http/Middleware/Api/ApiSubstituteBindings.php b/app/Http/Middleware/Api/ApiSubstituteBindings.php deleted file mode 100644 index 94af9b1d4..000000000 --- a/app/Http/Middleware/Api/ApiSubstituteBindings.php +++ /dev/null @@ -1,87 +0,0 @@ - Allocation::class, - 'database' => Database::class, - 'egg' => Egg::class, - 'location' => Location::class, - 'nest' => Nest::class, - 'node' => Node::class, - 'server' => Server::class, - 'user' => User::class, - ]; - - /** - * @var \Illuminate\Routing\Router - */ - protected $router; - - /** - * Perform substitution of route parameters without triggering - * a 404 error if a model is not found. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - */ - public function handle($request, Closure $next) - { - $route = $request->route(); - - foreach (self::$mappings as $key => $model) { - if (! is_null($this->router->getBindingCallback($key))) { - continue; - } - - $this->router->model($key, $model, function () use ($request) { - $request->attributes->set('is_missing_model', true); - }); - } - - $this->router->substituteBindings($route); - - // Attempt to resolve bindings for this route. If one of the models - // cannot be resolved do not immediately return a 404 error. Set a request - // attribute that can be checked in the base API request class to only - // trigger a 404 after validating that the API key making the request is valid - // and even has permission to access the requested resource. - try { - $this->router->substituteImplicitBindings($route); - } catch (ModelNotFoundException $exception) { - $request->attributes->set('is_missing_model', true); - } - - return $next($request); - } - - /** - * Return the registered mappings. - * - * @return array - */ - public static function getMappings() - { - return self::$mappings; - } -} diff --git a/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php b/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php index ddcd29484..e6f83b433 100644 --- a/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php +++ b/app/Http/Middleware/Api/Application/AuthenticateApplicationUser.php @@ -2,7 +2,6 @@ namespace Pterodactyl\Http\Middleware\Api\Application; -use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -11,14 +10,12 @@ class AuthenticateApplicationUser /** * Authenticate that the currently authenticated user is an administrator * and should be allowed to proceed through the application API. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { - if (is_null($request->user()) || ! $request->user()->root_admin) { + /** @var \Pterodactyl\Models\User|null $user */ + $user = $request->user(); + if (!$user || !$user->root_admin) { throw new AccessDeniedHttpException('This account does not have permission to access the API.'); } diff --git a/app/Http/Middleware/Api/AuthenticateIPAccess.php b/app/Http/Middleware/Api/AuthenticateIPAccess.php index aed8f53a4..b8a9937ab 100644 --- a/app/Http/Middleware/Api/AuthenticateIPAccess.php +++ b/app/Http/Middleware/Api/AuthenticateIPAccess.php @@ -2,10 +2,11 @@ namespace Pterodactyl\Http\Middleware\Api; -use Closure; use IPTools\IP; use IPTools\Range; use Illuminate\Http\Request; +use Pterodactyl\Facades\Activity; +use Laravel\Sanctum\TransientToken; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; class AuthenticateIPAccess @@ -13,28 +14,35 @@ class AuthenticateIPAccess /** * Determine if a request IP has permission to access the API. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * * @throws \Exception * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { - $model = $request->attributes->get('api_key'); + /** @var \Laravel\Sanctum\TransientToken|\Pterodactyl\Models\ApiKey $token */ + $token = $request->user()->currentAccessToken(); - if (is_null($model->allowed_ips) || empty($model->allowed_ips)) { + // If this is a stateful request just push the request through to the next + // middleware in the stack, there is nothing we need to explicitly check. If + // this is a valid API Key, but there is no allowed IP restriction, also pass + // the request through. + if ($token instanceof TransientToken || empty($token->allowed_ips)) { return $next($request); } $find = new IP($request->ip()); - foreach (json_decode($model->allowed_ips) as $ip) { + foreach ($token->allowed_ips as $ip) { if (Range::parse($ip)->contains($find)) { return $next($request); } } + Activity::event('auth:ip-blocked') + ->actor($request->user()) + ->subject($request->user(), $token) + ->property('identifier', $token->identifier) + ->log(); + throw new AccessDeniedHttpException('This IP address (' . $request->ip() . ') does not have permission to access the API using these credentials.'); } } diff --git a/app/Http/Middleware/Api/AuthenticateKey.php b/app/Http/Middleware/Api/AuthenticateKey.php deleted file mode 100644 index 8f400bb4d..000000000 --- a/app/Http/Middleware/Api/AuthenticateKey.php +++ /dev/null @@ -1,88 +0,0 @@ -auth = $auth; - $this->encrypter = $encrypter; - $this->repository = $repository; - } - - /** - * Handle an API request by verifying that the provided API key - * is in a valid format and exists in the database. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param int $keyType - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(Request $request, Closure $next, int $keyType) - { - if (is_null($request->bearerToken())) { - throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']); - } - - $raw = $request->bearerToken(); - $identifier = substr($raw, 0, ApiKey::IDENTIFIER_LENGTH); - $token = substr($raw, ApiKey::IDENTIFIER_LENGTH); - - try { - $model = $this->repository->findFirstWhere([ - ['identifier', '=', $identifier], - ['key_type', '=', $keyType], - ]); - } catch (RecordNotFoundException $exception) { - throw new AccessDeniedHttpException; - } - - if (! hash_equals($this->encrypter->decrypt($model->token), $token)) { - throw new AccessDeniedHttpException; - } - - $this->auth->guard()->loginUsingId($model->user_id); - $request->attributes->set('api_key', $model); - $this->repository->withoutFreshModel()->update($model->id, ['last_used_at' => Chronos::now()]); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Api/Client/AuthenticateClientAccess.php b/app/Http/Middleware/Api/Client/AuthenticateClientAccess.php deleted file mode 100644 index e048b5869..000000000 --- a/app/Http/Middleware/Api/Client/AuthenticateClientAccess.php +++ /dev/null @@ -1,60 +0,0 @@ -keyProviderService = $keyProviderService; - } - - /** - * Authenticate that the currently authenticated user has permission - * to access the specified server. This only checks that the user is an - * admin, owner, or a subuser. You'll need to do more specific checks in - * the API calls to determine if they can perform different actions. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function handle(Request $request, Closure $next) - { - if (is_null($request->user())) { - throw new AccessDeniedHttpException('A request must be made using an authenticated client.'); - } - - /** @var \Pterodactyl\Models\Server $server */ - $server = $request->route()->parameter('server'); - - try { - $token = $this->keyProviderService->handle($server, $request->user()); - } catch (RecordNotFoundException $exception) { - throw new NotFoundHttpException('The requested server could not be located.'); - } - - $request->attributes->set('server_token', $token); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Api/Client/RequireClientApiKey.php b/app/Http/Middleware/Api/Client/RequireClientApiKey.php new file mode 100644 index 000000000..1ddd0f128 --- /dev/null +++ b/app/Http/Middleware/Api/Client/RequireClientApiKey.php @@ -0,0 +1,25 @@ +user()->currentAccessToken(); + + if ($token instanceof ApiKey && $token->key_type === ApiKey::TYPE_APPLICATION) { + throw new AccessDeniedHttpException('You are attempting to use an application API key on an endpoint that requires a client API key.'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php new file mode 100644 index 000000000..f60b696c1 --- /dev/null +++ b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php @@ -0,0 +1,68 @@ +user(); + $server = $request->route()->parameter('server'); + + if (!$server instanceof Server) { + throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); + } + + // At the very least, ensure that the user trying to make this request is the + // server owner, a subuser, or a root admin. We'll leave it up to the controllers + // to authenticate more detailed permissions if needed. + if ($user->id !== $server->owner_id && !$user->root_admin) { + // Check for subuser status. + if (!$server->subusers->contains('user_id', $user->id)) { + throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); + } + } + + try { + $server->validateCurrentState(); + } catch (ServerStateConflictException $exception) { + // Still allow users to get information about their server if it is installing or + // being transferred. + if (!$request->routeIs('api:client:server.view')) { + if (($server->isSuspended() || $server->node->isUnderMaintenance()) && !$request->routeIs('api:client:server.resources')) { + throw $exception; + } + if (!$user->root_admin || !$request->routeIs($this->except)) { + throw $exception; + } + } + } + + $request->attributes->set('server', $server); + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Client/Server/ResourceBelongsToServer.php b/app/Http/Middleware/Api/Client/Server/ResourceBelongsToServer.php new file mode 100644 index 000000000..a98e17a08 --- /dev/null +++ b/app/Http/Middleware/Api/Client/Server/ResourceBelongsToServer.php @@ -0,0 +1,88 @@ +route()->parameters(); + if (!$params['server'] instanceof Server) { + throw new \InvalidArgumentException('This middleware cannot be used in a context that is missing a server in the parameters.'); + } + + /** @var Server $server */ + $server = $request->route()->parameter('server'); + $exception = new NotFoundHttpException('The requested resource was not found for this server.'); + foreach ($params as $key => $model) { + // Specifically skip the server, we're just trying to see if all of the + // other resources are assigned to this server. Also skip anything that + // is not currently a Model instance since those will just end up being + // a 404 down the road. + if ($key === 'server' || !$model instanceof Model) { + continue; + } + + /** @var Allocation|Backup|Database|Schedule|Subuser $model */ + switch (get_class($model)) { + // All of these models use "server_id" as the field key for the server + // they are assigned to, so the logic is identical for them all. + case Allocation::class: + case Backup::class: + case Database::class: + case Schedule::class: + case Subuser::class: + if ($model->server_id !== $server->id) { + throw $exception; + } + break; + // Regular users are a special case here as we need to make sure they're + // currently assigned as a subuser on the server. + case User::class: + $subuser = $server->subusers()->where('user_id', $model->id)->first(); + if (is_null($subuser)) { + throw $exception; + } + // This is a special case to avoid an additional query being triggered + // in the underlying logic. + $request->attributes->set('subuser', $subuser); + break; + // Tasks are special since they're (currently) the only item in the API + // that requires something in addition to the server in order to be accessed. + case Task::class: + /** @var Schedule $schedule */ + $schedule = $request->route()->parameter('schedule'); + if ($model->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) { + throw $exception; + } + break; + default: + // Don't return a 404 here since we want to make sure no one relies + // on this middleware in a context in which it will not work. Fail safe. + throw new \InvalidArgumentException('There is no handler configured for a resource of this type: ' . get_class($model)); + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/Client/SubstituteClientApiBindings.php b/app/Http/Middleware/Api/Client/SubstituteClientApiBindings.php deleted file mode 100644 index f8a35fdd8..000000000 --- a/app/Http/Middleware/Api/Client/SubstituteClientApiBindings.php +++ /dev/null @@ -1,39 +0,0 @@ -router->bind('server', function ($value) use ($request) { - try { - return Container::getInstance()->make(ServerRepositoryInterface::class)->findFirstWhere([ - ['uuidShort', '=', $value], - ]); - } catch (RecordNotFoundException $ex) { - $request->attributes->set('is_missing_model', true); - - return null; - } - }); - - return parent::handle($request, $next); - } -} diff --git a/app/Http/Middleware/Api/Client/SubstituteClientBindings.php b/app/Http/Middleware/Api/Client/SubstituteClientBindings.php new file mode 100644 index 000000000..af681467d --- /dev/null +++ b/app/Http/Middleware/Api/Client/SubstituteClientBindings.php @@ -0,0 +1,33 @@ +router->bind('server', function ($value) { + return Server::query()->where(strlen($value) === 8 ? 'uuidShort' : 'uuid', $value)->firstOrFail(); + }); + + $this->router->bind('user', function ($value, $route) { + /** @var \Pterodactyl\Models\Subuser $match */ + $match = $route->parameter('server') + ->subusers() + ->whereRelation('user', 'uuid', '=', $value) + ->firstOrFail(); + + return $match->user; + }); + + return parent::handle($request, $next); + } +} diff --git a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php index c951b1b4a..6e5ae2a43 100644 --- a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php +++ b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php @@ -2,68 +2,66 @@ namespace Pterodactyl\Http\Middleware\Api\Daemon; -use Closure; use Illuminate\Http\Request; +use Illuminate\Contracts\Encryption\Encrypter; +use Pterodactyl\Repositories\Eloquent\NodeRepository; use Symfony\Component\HttpKernel\Exception\HttpException; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; class DaemonAuthenticate { - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - private $repository; - /** * Daemon routes that this middleware should be skipped on. - * - * @var array */ - protected $except = [ + protected array $except = [ 'daemon.configuration', ]; /** * DaemonAuthenticate constructor. - * - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository */ - public function __construct(NodeRepositoryInterface $repository) + public function __construct(private Encrypter $encrypter, private NodeRepository $repository) { - $this->repository = $repository; } /** * Check if a request from the daemon can be properly attributed back to a single node instance. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { if (in_array($request->route()->getName(), $this->except)) { return $next($request); } - $token = $request->bearerToken(); + if (is_null($bearer = $request->bearerToken())) { + throw new HttpException(401, 'Access to this endpoint must include an Authorization header.', null, ['WWW-Authenticate' => 'Bearer']); + } - if (is_null($token)) { - throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']); + $parts = explode('.', $bearer); + // Ensure that all of the correct parts are provided in the header. + if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) { + throw new BadRequestHttpException('The Authorization header provided was not in a valid format.'); } try { - $node = $this->repository->findFirstWhere([['daemonSecret', '=', $token]]); + /** @var \Pterodactyl\Models\Node $node */ + $node = $this->repository->findFirstWhere([ + 'daemon_token_id' => $parts[0], + ]); + + if (hash_equals((string) $this->encrypter->decrypt($node->daemon_token), $parts[1])) { + $request->attributes->set('node', $node); + + return $next($request); + } } catch (RecordNotFoundException $exception) { - throw new AccessDeniedHttpException; + // Do nothing, we don't want to expose a node not existing at all. } - $request->attributes->set('node', $node); - - return $next($request); + throw new AccessDeniedHttpException('You are not authorized to access this resource.'); } } diff --git a/app/Http/Middleware/Api/IsValidJson.php b/app/Http/Middleware/Api/IsValidJson.php new file mode 100644 index 000000000..95101a1b2 --- /dev/null +++ b/app/Http/Middleware/Api/IsValidJson.php @@ -0,0 +1,27 @@ +isJson() && !empty($request->getContent())) { + try { + json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new BadRequestHttpException('The JSON data passed in the request appears to be malformed: ' . $exception->getMessage()); + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/Api/SetSessionDriver.php b/app/Http/Middleware/Api/SetSessionDriver.php deleted file mode 100644 index 3d5c16617..000000000 --- a/app/Http/Middleware/Api/SetSessionDriver.php +++ /dev/null @@ -1,52 +0,0 @@ -app = $app; - $this->config = $config; - } - - /** - * Set the session for API calls to only last for the one request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - */ - public function handle(Request $request, Closure $next) - { - if ($this->config->get('app.debug')) { - $this->app->make(LaravelDebugbar::class)->disable(); - } - - $this->config->set('session.driver', 'array'); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php deleted file mode 100644 index d85cf95bf..000000000 --- a/app/Http/Middleware/Authenticate.php +++ /dev/null @@ -1,28 +0,0 @@ -user()) { - throw new AuthenticationException; - } - - return $next($request); - } -} diff --git a/app/Http/Middleware/DaemonAuthenticate.php b/app/Http/Middleware/DaemonAuthenticate.php deleted file mode 100644 index b8e83bf8c..000000000 --- a/app/Http/Middleware/DaemonAuthenticate.php +++ /dev/null @@ -1,69 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Middleware; - -use Closure; -use Illuminate\Http\Request; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; - -class DaemonAuthenticate -{ - /** - * An array of route names to not apply this middleware to. - * - * @var array - */ - private $except = [ - 'daemon.configuration', - ]; - - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - private $repository; - - /** - * Create a new filter instance. - * - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository - * @deprecated - */ - public function __construct(NodeRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - */ - public function handle(Request $request, Closure $next) - { - if (in_array($request->route()->getName(), $this->except)) { - return $next($request); - } - - if (! $request->header('X-Access-Node')) { - throw new AccessDeniedHttpException; - } - - $node = $this->repository->findFirstWhere(['daemonSecret' => $request->header('X-Access-Node')]); - $request->attributes->set('node', $node); - - return $next($request); - } -} diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index 9c0cadd86..1ac425a33 100644 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -8,8 +8,6 @@ class EncryptCookies extends BaseEncrypter { /** * The names of the cookies that should not be encrypted. - * - * @var array */ protected $except = []; } diff --git a/app/Http/Middleware/EnsureStatefulRequests.php b/app/Http/Middleware/EnsureStatefulRequests.php new file mode 100644 index 000000000..db6e19ae9 --- /dev/null +++ b/app/Http/Middleware/EnsureStatefulRequests.php @@ -0,0 +1,26 @@ +hasCookie(config('session.cookie')); + } +} diff --git a/app/Http/Middleware/LanguageMiddleware.php b/app/Http/Middleware/LanguageMiddleware.php index 65692d3e2..e98ad2863 100644 --- a/app/Http/Middleware/LanguageMiddleware.php +++ b/app/Http/Middleware/LanguageMiddleware.php @@ -2,35 +2,22 @@ namespace Pterodactyl\Http\Middleware; -use Closure; use Illuminate\Http\Request; use Illuminate\Foundation\Application; class LanguageMiddleware { - /** - * @var \Illuminate\Foundation\Application - */ - private $app; - /** * LanguageMiddleware constructor. - * - * @param \Illuminate\Foundation\Application $app */ - public function __construct(Application $app) + public function __construct(private Application $app) { - $this->app = $app; } /** * Handle an incoming request and set the user's preferred language. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { $this->app->setLocale($request->user()->language ?? config('app.locale', 'en')); diff --git a/app/Http/Middleware/MaintenanceMiddleware.php b/app/Http/Middleware/MaintenanceMiddleware.php index c67a3f051..61247b4e0 100644 --- a/app/Http/Middleware/MaintenanceMiddleware.php +++ b/app/Http/Middleware/MaintenanceMiddleware.php @@ -2,34 +2,22 @@ namespace Pterodactyl\Http\Middleware; -use Closure; +use Illuminate\Http\Request; use Illuminate\Contracts\Routing\ResponseFactory; class MaintenanceMiddleware { - /** - * @var \Illuminate\Contracts\Routing\ResponseFactory - */ - private $response; - /** * MaintenanceMiddleware constructor. - * - * @param \Illuminate\Contracts\Routing\ResponseFactory $response */ - public function __construct(ResponseFactory $response) + public function __construct(private ResponseFactory $response) { - $this->response = $response; } /** * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed */ - public function handle($request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { /** @var \Pterodactyl\Models\Server $server */ $server = $request->attributes->get('server'); diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 8a5220cb5..ad3eecdc7 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -2,36 +2,22 @@ namespace Pterodactyl\Http\Middleware; -use Closure; use Illuminate\Http\Request; use Illuminate\Auth\AuthManager; class RedirectIfAuthenticated { - /** - * @var \Illuminate\Auth\AuthManager - */ - private $authManager; - /** * RedirectIfAuthenticated constructor. - * - * @param \Illuminate\Auth\AuthManager $authManager */ - public function __construct(AuthManager $authManager) + public function __construct(private AuthManager $authManager) { - $this->authManager = $authManager; } /** * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string|null $guard - * @return mixed */ - public function handle(Request $request, Closure $next, string $guard = null) + public function handle(Request $request, \Closure $next, string $guard = null): mixed { if ($this->authManager->guard($guard)->check()) { return redirect()->route('index'); diff --git a/app/Http/Middleware/RequireTwoFactorAuthentication.php b/app/Http/Middleware/RequireTwoFactorAuthentication.php index aee1cf068..a6110edb1 100644 --- a/app/Http/Middleware/RequireTwoFactorAuthentication.php +++ b/app/Http/Middleware/RequireTwoFactorAuthentication.php @@ -1,96 +1,63 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Http\Middleware; -use Closure; +use Illuminate\Support\Str; use Illuminate\Http\Request; -use Prologue\Alerts\AlertsMessageBag; +use Pterodactyl\Models\User; +use Pterodactyl\Exceptions\Http\TwoFactorAuthRequiredException; class RequireTwoFactorAuthentication { - const LEVEL_NONE = 0; - const LEVEL_ADMIN = 1; - const LEVEL_ALL = 2; + public const LEVEL_NONE = 0; + public const LEVEL_ADMIN = 1; + public const LEVEL_ALL = 2; /** - * @var \Prologue\Alerts\AlertsMessageBag + * The route to redirect a user to enable 2FA. */ - private $alert; + protected string $redirectRoute = '/account'; /** - * The names of routes that should be accessible without 2FA enabled. + * Check the user state on the incoming request to determine if they should be allowed to + * proceed or not. This checks if the Panel is configured to require 2FA on an account in + * order to perform actions. If so, we check the level at which it is required (all users + * or just admins) and then check if the user has enabled it for their account. * - * @var array + * @throws \Pterodactyl\Exceptions\Http\TwoFactorAuthRequiredException */ - protected $except = [ - 'account.security', - 'account.security.revoke', - 'account.security.totp', - 'account.security.totp.set', - 'account.security.totp.disable', - 'auth.totp', - 'auth.logout', - ]; - - /** - * The route to redirect a user to to enable 2FA. - * - * @var string - */ - protected $redirectRoute = 'account.security'; - - /** - * RequireTwoFactorAuthentication constructor. - * - * @param \Prologue\Alerts\AlertsMessageBag $alert - */ - public function __construct(AlertsMessageBag $alert) + public function handle(Request $request, \Closure $next): mixed { - $this->alert = $alert; - } + /** @var User $user */ + $user = $request->user(); + $uri = rtrim($request->getRequestUri(), '/') . '/'; + $current = $request->route()->getName(); - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - */ - public function handle(Request $request, Closure $next) - { - if (! $request->user()) { + // Must be logged in + if (!$user instanceof User) { return $next($request); } - if (in_array($request->route()->getName(), $this->except)) { + if (Str::startsWith($uri, ['/auth/']) || Str::startsWith($current, ['auth.', 'account.'])) { return $next($request); } - switch ((int) config('pterodactyl.auth.2fa_required')) { - case self::LEVEL_ADMIN: - if (! $request->user()->root_admin || $request->user()->use_totp) { - return $next($request); - } - break; - case self::LEVEL_ALL: - if ($request->user()->use_totp) { - return $next($request); - } - break; - case self::LEVEL_NONE: - default: - return $next($request); + $level = (int) config('pterodactyl.auth.2fa_required'); + // If this setting is not configured, or the user is already using 2FA then we can just + // send them right through, nothing else needs to be checked. + // + // If the level is set as admin and the user is not an admin, pass them through as well. + if ($level === self::LEVEL_NONE || $user->use_totp) { + return $next($request); + } elseif ($level === self::LEVEL_ADMIN && !$user->root_admin) { + return $next($request); } - $this->alert->danger(trans('auth.2fa_must_be_enabled'))->flash(); + // For API calls return an exception which gets rendered nicely in the API response. + if ($request->isJson() || Str::startsWith($uri, '/api/')) { + throw new TwoFactorAuthRequiredException(); + } - return redirect()->route($this->redirectRoute); + return redirect()->to($this->redirectRoute); } } diff --git a/app/Http/Middleware/Server/AccessingValidServer.php b/app/Http/Middleware/Server/AccessingValidServer.php deleted file mode 100644 index ea29e0b99..000000000 --- a/app/Http/Middleware/Server/AccessingValidServer.php +++ /dev/null @@ -1,103 +0,0 @@ -config = $config; - $this->repository = $repository; - $this->response = $response; - $this->session = $session; - } - - /** - * Determine if a given user has permission to access a server. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return \Illuminate\Http\Response|mixed - * - * @throws \Illuminate\Auth\AuthenticationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - */ - public function handle(Request $request, Closure $next) - { - $attributes = $request->route()->parameter('server'); - $isApiRequest = $request->expectsJson() || $request->is(...$this->config->get('pterodactyl.json_routes', [])); - $server = $this->repository->getByUuid($attributes instanceof Server ? $attributes->uuid : $attributes); - - if ($server->suspended) { - if ($isApiRequest) { - throw new AccessDeniedHttpException('Server is suspended and cannot be accessed.'); - } - - return $this->response->view('errors.suspended', [], 403); - } - - // Servers can have install statuses other than 1 or 0, so don't check - // for a bool-type operator here. - if ($server->installed !== 1) { - if ($isApiRequest) { - throw new ConflictHttpException('Server is still completing the installation process.'); - } - - return $this->response->view('errors.installing', [], 409); - } - - // Store the server in the session. - // @todo remove from session. use request attributes. - $this->session->now('server_data.model', $server); - - // Add server to the request attributes. This will replace sessions - // as files are updated. - $request->attributes->set('server', $server); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Server/AuthenticateAsSubuser.php b/app/Http/Middleware/Server/AuthenticateAsSubuser.php deleted file mode 100644 index ebf9cd622..000000000 --- a/app/Http/Middleware/Server/AuthenticateAsSubuser.php +++ /dev/null @@ -1,59 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Middleware\Server; - -use Closure; -use Illuminate\Http\Request; -use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; - -class AuthenticateAsSubuser -{ - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService - */ - private $keyProviderService; - - /** - * SubuserAccessAuthenticate constructor. - * - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService - */ - public function __construct(DaemonKeyProviderService $keyProviderService) - { - $this->keyProviderService = $keyProviderService; - } - - /** - * Determine if a subuser has permissions to access a server, if so set their access token. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - */ - public function handle(Request $request, Closure $next) - { - $server = $request->attributes->get('server'); - - try { - $token = $this->keyProviderService->handle($server, $request->user()); - } catch (RecordNotFoundException $exception) { - throw new AccessDeniedHttpException('This account does not have permission to access this server.'); - } - - $request->attributes->set('server_token', $token); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Server/DatabaseBelongsToServer.php b/app/Http/Middleware/Server/DatabaseBelongsToServer.php deleted file mode 100644 index 1617a8520..000000000 --- a/app/Http/Middleware/Server/DatabaseBelongsToServer.php +++ /dev/null @@ -1,56 +0,0 @@ -repository = $repository; - } - - /** - * Check if a database being requested belongs to the currently loaded server. - * If it does not, throw a 404 error, otherwise continue on with the request - * and set an attribute with the database. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(Request $request, Closure $next) - { - $server = $request->attributes->get('server'); - $database = $request->input('database') ?? $request->route()->parameter('database'); - - if (! is_digit($database)) { - throw new NotFoundHttpException; - } - - $database = $this->repository->find($database); - if (is_null($database) || $database->server_id !== $server->id) { - throw new NotFoundHttpException; - } - - $request->attributes->set('database', $database); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Server/ScheduleBelongsToServer.php b/app/Http/Middleware/Server/ScheduleBelongsToServer.php deleted file mode 100644 index 26da5f843..000000000 --- a/app/Http/Middleware/Server/ScheduleBelongsToServer.php +++ /dev/null @@ -1,60 +0,0 @@ -hashids = $hashids; - $this->repository = $repository; - } - - /** - * Determine if a task is assigned to the active server. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - */ - public function handle(Request $request, Closure $next) - { - $server = $request->attributes->get('server'); - - $scheduleId = $this->hashids->decodeFirst($request->route()->parameter('schedule'), 0); - $schedule = $this->repository->getScheduleWithTasks($scheduleId); - - if ($schedule->server_id !== $server->id) { - throw new NotFoundHttpException; - } - - $request->attributes->set('schedule', $schedule); - - return $next($request); - } -} diff --git a/app/Http/Middleware/Server/SubuserBelongsToServer.php b/app/Http/Middleware/Server/SubuserBelongsToServer.php deleted file mode 100644 index cdcd3f097..000000000 --- a/app/Http/Middleware/Server/SubuserBelongsToServer.php +++ /dev/null @@ -1,67 +0,0 @@ -hashids = $hashids; - $this->repository = $repository; - } - - /** - * Determine if a user has permission to access and modify subuser. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - */ - public function handle(Request $request, Closure $next) - { - $server = $request->attributes->get('server'); - - $hash = $request->route()->parameter('subuser', 0); - $subuser = $this->repository->find($this->hashids->decodeFirst($hash, 0)); - if (is_null($subuser) || $subuser->server_id !== $server->id) { - throw new NotFoundHttpException; - } - - if ($request->method() === 'PATCH') { - if ($subuser->user_id === $request->user()->id) { - throw new DisplayException(trans('exceptions.subusers.editing_self')); - } - } - - $request->attributes->set('subuser', $subuser); - - return $next($request); - } -} diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index 04f434b98..af5382e8c 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -8,8 +8,6 @@ class TrimStrings extends BaseTrimmer { /** * The names of the attributes that should not be trimmed. - * - * @var array */ protected $except = [ 'password', diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php deleted file mode 100644 index 3dff63eeb..000000000 --- a/app/Http/Middleware/TrustProxies.php +++ /dev/null @@ -1,23 +0,0 @@ -config = $config; } /** * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return \Illuminate\Http\RedirectResponse|mixed */ - public function handle($request, Closure $next) + public function handle(Request $request, \Closure $next): mixed { - if (! $this->config->get('recaptcha.enabled')) { + if (!$this->config->get('recaptcha.enabled')) { return $next($request); } @@ -51,28 +40,28 @@ class VerifyReCaptcha if ($res->getStatusCode() === 200) { $result = json_decode($res->getBody()); - if ($result->success && (! $this->config->get('recaptcha.verify_domain') || $this->isResponseVerified($result, $request))) { + if ($result->success && (!$this->config->get('recaptcha.verify_domain') || $this->isResponseVerified($result, $request))) { return $next($request); } } } - // Emit an event and return to the previous view with an error (only the captcha error will be shown!) - event(new FailedCaptcha($request->ip(), (! isset($result) ?: object_get($result, 'hostname')))); + $this->dispatcher->dispatch( + new FailedCaptcha( + $request->ip(), + !empty($result) ? ($result->hostname ?? null) : null + ) + ); - return redirect()->back()->withErrors(['g-recaptcha-response' => trans('strings.captcha_invalid')])->withInput(); + throw new HttpException(Response::HTTP_BAD_REQUEST, 'Failed to validate reCAPTCHA data.'); } /** * Determine if the response from the recaptcha servers was valid. - * - * @param stdClass $result - * @param \Illuminate\Http\Request $request - * @return bool */ - private function isResponseVerified(stdClass $result, Request $request): bool + private function isResponseVerified(\stdClass $result, Request $request): bool { - if (! $this->config->get('recaptcha.verify_domain')) { + if (!$this->config->get('recaptcha.verify_domain')) { return false; } diff --git a/app/Http/Requests/Admin/AdminFormRequest.php b/app/Http/Requests/Admin/AdminFormRequest.php deleted file mode 100644 index 012d73364..000000000 --- a/app/Http/Requests/Admin/AdminFormRequest.php +++ /dev/null @@ -1,42 +0,0 @@ -user())) { - return false; - } - - return (bool) $this->user()->root_admin; - } - - /** - * Return only the fields that we are interested in from the request. - * This will include empty fields as a null value. - * - * @param array|null $only - * @return array - */ - public function normalize(array $only = null) - { - return $this->only($only ?? array_keys($this->rules())); - } -} diff --git a/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php b/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php deleted file mode 100644 index 2549517cf..000000000 --- a/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php +++ /dev/null @@ -1,41 +0,0 @@ -mapWithKeys(function ($resource) use ($modelRules) { - return [AdminAcl::COLUMN_IDENTIFIER . $resource => $modelRules['r_' . $resource]]; - })->merge(['memo' => $modelRules['memo']])->toArray(); - } - - /** - * @return array - */ - public function attributes() - { - return [ - 'memo' => 'Description', - ]; - } - - public function getKeyPermissions(): array - { - return collect($this->validated())->filter(function ($value, $key) { - return substr($key, 0, strlen(AdminAcl::COLUMN_IDENTIFIER)) === AdminAcl::COLUMN_IDENTIFIER; - })->toArray(); - } -} diff --git a/app/Http/Requests/Admin/BaseFormRequest.php b/app/Http/Requests/Admin/BaseFormRequest.php deleted file mode 100644 index dff6b9fb2..000000000 --- a/app/Http/Requests/Admin/BaseFormRequest.php +++ /dev/null @@ -1,20 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin; - -class BaseFormRequest extends AdminFormRequest -{ - public function rules() - { - return [ - 'company' => 'required|between:1,256', - ]; - } -} diff --git a/app/Http/Requests/Admin/DatabaseHostFormRequest.php b/app/Http/Requests/Admin/DatabaseHostFormRequest.php deleted file mode 100644 index a7b0b4aff..000000000 --- a/app/Http/Requests/Admin/DatabaseHostFormRequest.php +++ /dev/null @@ -1,38 +0,0 @@ -method() !== 'POST') { - return DatabaseHost::getUpdateRulesForId($this->route()->parameter('host')); - } - - return DatabaseHost::getCreateRules(); - } - - /** - * Modify submitted data before it is passed off to the validator. - * - * @return \Illuminate\Contracts\Validation\Validator - */ - protected function getValidatorInstance() - { - if (! $this->filled('node_id')) { - $this->merge(['node_id' => null]); - } - - $this->merge([ - 'host' => gethostbyname($this->input('host')), - ]); - - return parent::getValidatorInstance(); - } -} diff --git a/app/Http/Requests/Admin/Egg/EggFormRequest.php b/app/Http/Requests/Admin/Egg/EggFormRequest.php deleted file mode 100644 index 539ee3adc..000000000 --- a/app/Http/Requests/Admin/Egg/EggFormRequest.php +++ /dev/null @@ -1,49 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Egg; - -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class EggFormRequest extends AdminFormRequest -{ - /** - * {@inheritdoc} - */ - public function rules() - { - $rules = [ - 'name' => 'required|string|max:255', - 'description' => 'required|string', - 'docker_image' => 'required|string|max:255', - 'startup' => 'required|string', - 'config_from' => 'sometimes|bail|nullable|numeric', - 'config_stop' => 'required_without:config_from|nullable|string|max:255', - 'config_startup' => 'required_without:config_from|nullable|json', - 'config_logs' => 'required_without:config_from|nullable|json', - 'config_files' => 'required_without:config_from|nullable|json', - ]; - - if ($this->method() === 'POST') { - $rules['nest_id'] = 'required|numeric|exists:nests,id'; - } - - return $rules; - } - - /** - * @param \Illuminate\Contracts\Validation\Validator $validator - */ - public function withValidator($validator) - { - $validator->sometimes('config_from', 'exists:eggs,id', function () { - return (int) $this->input('config_from') !== 0; - }); - } -} diff --git a/app/Http/Requests/Admin/Egg/EggImportFormRequest.php b/app/Http/Requests/Admin/Egg/EggImportFormRequest.php deleted file mode 100644 index b6adb768e..000000000 --- a/app/Http/Requests/Admin/Egg/EggImportFormRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Egg; - -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class EggImportFormRequest extends AdminFormRequest -{ - /** - * @return array - */ - public function rules() - { - $rules = [ - 'import_file' => 'bail|required|file|max:1000|mimetypes:application/json,text/plain', - ]; - - if ($this->method() !== 'PUT') { - $rules['import_to_nest'] = 'bail|required|integer|exists:nests,id'; - } - - return $rules; - } -} diff --git a/app/Http/Requests/Admin/Egg/EggScriptFormRequest.php b/app/Http/Requests/Admin/Egg/EggScriptFormRequest.php deleted file mode 100644 index 3f522e96f..000000000 --- a/app/Http/Requests/Admin/Egg/EggScriptFormRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Egg; - -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class EggScriptFormRequest extends AdminFormRequest -{ - /** - * Return the rules to be used when validating the sent data in the request. - * - * @return array - */ - public function rules() - { - return [ - 'script_install' => 'sometimes|nullable|string', - 'script_is_privileged' => 'sometimes|required|boolean', - 'script_entry' => 'sometimes|required|string', - 'script_container' => 'sometimes|required|string', - 'copy_script_from' => 'sometimes|nullable|numeric', - ]; - } -} diff --git a/app/Http/Requests/Admin/Egg/EggVariableFormRequest.php b/app/Http/Requests/Admin/Egg/EggVariableFormRequest.php deleted file mode 100644 index 933bf8348..000000000 --- a/app/Http/Requests/Admin/Egg/EggVariableFormRequest.php +++ /dev/null @@ -1,26 +0,0 @@ - 'required|string|min:1|max:255', - 'description' => 'sometimes|nullable|string', - 'env_variable' => 'required|regex:/^[\w]{1,255}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES, - 'options' => 'sometimes|required|array', - 'rules' => 'bail|required|string', - 'default_value' => 'present', - ]; - } -} diff --git a/app/Http/Requests/Admin/LocationFormRequest.php b/app/Http/Requests/Admin/LocationFormRequest.php deleted file mode 100644 index 16d80a253..000000000 --- a/app/Http/Requests/Admin/LocationFormRequest.php +++ /dev/null @@ -1,29 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin; - -use Pterodactyl\Models\Location; - -class LocationFormRequest extends AdminFormRequest -{ - /** - * Setup the validation rules to use for these requests. - * - * @return array - */ - public function rules() - { - if ($this->method() === 'PATCH') { - return Location::getUpdateRulesForId($this->route()->parameter('location')->id); - } - - return Location::getCreateRules(); - } -} diff --git a/app/Http/Requests/Admin/Nest/StoreNestFormRequest.php b/app/Http/Requests/Admin/Nest/StoreNestFormRequest.php deleted file mode 100644 index 56255e558..000000000 --- a/app/Http/Requests/Admin/Nest/StoreNestFormRequest.php +++ /dev/null @@ -1,26 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Nest; - -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class StoreNestFormRequest extends AdminFormRequest -{ - /** - * @return array - */ - public function rules() - { - return [ - 'name' => 'required|string|min:1|max:255', - 'description' => 'required|nullable|string', - ]; - } -} diff --git a/app/Http/Requests/Admin/Node/AllocationAliasFormRequest.php b/app/Http/Requests/Admin/Node/AllocationAliasFormRequest.php deleted file mode 100644 index 2552114ab..000000000 --- a/app/Http/Requests/Admin/Node/AllocationAliasFormRequest.php +++ /dev/null @@ -1,26 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Node; - -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class AllocationAliasFormRequest extends AdminFormRequest -{ - /** - * @return array - */ - public function rules() - { - return [ - 'alias' => 'present|nullable|string', - 'allocation_id' => 'required|numeric|exists:allocations,id', - ]; - } -} diff --git a/app/Http/Requests/Admin/Node/AllocationFormRequest.php b/app/Http/Requests/Admin/Node/AllocationFormRequest.php deleted file mode 100644 index 777d3033f..000000000 --- a/app/Http/Requests/Admin/Node/AllocationFormRequest.php +++ /dev/null @@ -1,27 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Node; - -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class AllocationFormRequest extends AdminFormRequest -{ - /** - * @return array - */ - public function rules() - { - return [ - 'allocation_ip' => 'required|string', - 'allocation_alias' => 'sometimes|nullable|string|max:255', - 'allocation_ports' => 'required|array', - ]; - } -} diff --git a/app/Http/Requests/Admin/Node/NodeFormRequest.php b/app/Http/Requests/Admin/Node/NodeFormRequest.php deleted file mode 100644 index fcedd4275..000000000 --- a/app/Http/Requests/Admin/Node/NodeFormRequest.php +++ /dev/null @@ -1,48 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin\Node; - -use Pterodactyl\Models\Node; -use Pterodactyl\Http\Requests\Admin\AdminFormRequest; - -class NodeFormRequest extends AdminFormRequest -{ - /** - * Get rules to apply to data in this request. - */ - public function rules() - { - if ($this->method() === 'PATCH') { - return Node::getUpdateRulesForId($this->route()->parameter('node')->id); - } - - return Node::getCreateRules(); - } - - /** - * Run validation after the rules above have been applied. - * - * @param \Illuminate\Validation\Validator $validator - */ - public function withValidator($validator) - { - $validator->after(function ($validator) { - // Check that the FQDN is a valid IP address. - if (! filter_var(gethostbyname($this->input('fqdn')), FILTER_VALIDATE_IP)) { - $validator->errors()->add('fqdn', trans('admin/node.validation.fqdn_not_resolvable')); - } - - // Check that if using HTTPS the FQDN is not an IP address. - if (filter_var($this->input('fqdn'), FILTER_VALIDATE_IP) && $this->input('scheme') === 'https') { - $validator->errors()->add('fqdn', trans('admin/node.validation.fqdn_required_for_ssl')); - } - }); - } -} diff --git a/app/Http/Requests/Admin/PackFormRequest.php b/app/Http/Requests/Admin/PackFormRequest.php deleted file mode 100644 index 7f15be958..000000000 --- a/app/Http/Requests/Admin/PackFormRequest.php +++ /dev/null @@ -1,49 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin; - -use Pterodactyl\Models\Pack; -use Pterodactyl\Services\Packs\PackCreationService; - -class PackFormRequest extends AdminFormRequest -{ - /** - * @return array - */ - public function rules() - { - if ($this->method() === 'PATCH') { - return Pack::getUpdateRulesForId($this->route()->parameter('pack')->id); - } - - return Pack::getCreateRules(); - } - - /** - * Run validation after the rules above have been applied. - * - * @param \Illuminate\Validation\Validator $validator - */ - public function withValidator($validator) - { - if ($this->method() !== 'POST') { - return; - } - - $validator->after(function ($validator) { - $mimetypes = implode(',', PackCreationService::VALID_UPLOAD_TYPES); - - /* @var $validator \Illuminate\Validation\Validator */ - $validator->sometimes('file_upload', 'sometimes|required|file|mimetypes:' . $mimetypes, function () { - return true; - }); - }); - } -} diff --git a/app/Http/Requests/Admin/ServerFormRequest.php b/app/Http/Requests/Admin/ServerFormRequest.php deleted file mode 100644 index 33b9c8ffd..000000000 --- a/app/Http/Requests/Admin/ServerFormRequest.php +++ /dev/null @@ -1,76 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin; - -use Pterodactyl\Models\Server; -use Illuminate\Validation\Rule; - -class ServerFormRequest extends AdminFormRequest -{ - /** - * Rules to be applied to this request. - * - * @return array - */ - public function rules() - { - $rules = Server::getCreateRules(); - $rules['description'][] = 'nullable'; - - return $rules; - } - - /** - * Run validation after the rules above have been applied. - * - * @param \Illuminate\Validation\Validator $validator - */ - public function withValidator($validator) - { - $validator->after(function ($validator) { - $validator->sometimes('node_id', 'required|numeric|bail|exists:nodes,id', function ($input) { - return ! ($input->auto_deploy); - }); - - $validator->sometimes('allocation_id', [ - 'required', - 'numeric', - 'bail', - Rule::exists('allocations', 'id')->where(function ($query) { - $query->where('node_id', $this->input('node_id')); - $query->whereNull('server_id'); - }), - ], function ($input) { - return ! ($input->auto_deploy); - }); - - $validator->sometimes('allocation_additional.*', [ - 'sometimes', - 'required', - 'numeric', - Rule::exists('allocations', 'id')->where(function ($query) { - $query->where('node_id', $this->input('node_id')); - $query->whereNull('server_id'); - }), - ], function ($input) { - return ! ($input->auto_deploy); - }); - - $validator->sometimes('pack_id', [ - Rule::exists('packs', 'id')->where(function ($query) { - $query->where('selectable', 1); - $query->where('egg_id', $this->input('egg_id')); - }), - ], function ($input) { - return $input->pack_id !== 0 && $input->pack_id !== null; - }); - }); - } -} diff --git a/app/Http/Requests/Admin/Servers/Databases/StoreServerDatabaseRequest.php b/app/Http/Requests/Admin/Servers/Databases/StoreServerDatabaseRequest.php deleted file mode 100644 index ba8b76a83..000000000 --- a/app/Http/Requests/Admin/Servers/Databases/StoreServerDatabaseRequest.php +++ /dev/null @@ -1,32 +0,0 @@ - [ - 'required', - 'string', - 'min:1', - 'max:24', - Rule::unique('databases')->where(function (Builder $query) { - $query->where('database_host_id', $this->input('database_host_id') ?? 0); - }), - ], - 'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/', - 'database_host_id' => 'required|integer|exists:database_hosts,id', - ]; - } -} diff --git a/app/Http/Requests/Admin/Settings/AdvancedSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/AdvancedSettingsFormRequest.php deleted file mode 100644 index a80d8dab9..000000000 --- a/app/Http/Requests/Admin/Settings/AdvancedSettingsFormRequest.php +++ /dev/null @@ -1,42 +0,0 @@ - 'required|in:true,false', - 'recaptcha:secret_key' => 'required|string|max:255', - 'recaptcha:website_key' => 'required|string|max:255', - 'pterodactyl:guzzle:timeout' => 'required|integer|between:1,60', - 'pterodactyl:guzzle:connect_timeout' => 'required|integer|between:1,60', - 'pterodactyl:console:count' => 'required|integer|min:1', - 'pterodactyl:console:frequency' => 'required|integer|min:10', - ]; - } - - /** - * @return array - */ - public function attributes() - { - return [ - 'recaptcha:enabled' => 'reCAPTCHA Enabled', - 'recaptcha:secret_key' => 'reCAPTCHA Secret Key', - 'recaptcha:website_key' => 'reCAPTCHA Website Key', - 'pterodactyl:guzzle:timeout' => 'HTTP Request Timeout', - 'pterodactyl:guzzle:connect_timeout' => 'HTTP Connection Timeout', - 'pterodactyl:console:count' => 'Console Message Count', - 'pterodactyl:console:frequency' => 'Console Frequency Tick', - ]; - } -} diff --git a/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php deleted file mode 100644 index 0b02561dd..000000000 --- a/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php +++ /dev/null @@ -1,36 +0,0 @@ - 'required|string|max:255', - 'pterodactyl:auth:2fa_required' => 'required|integer|in:0,1,2', - 'app:locale' => ['required', 'string', Rule::in(array_keys($this->getAvailableLanguages()))], - ]; - } - - /** - * @return array - */ - public function attributes() - { - return [ - 'app:name' => 'Company Name', - 'pterodactyl:auth:2fa_required' => 'Require 2-Factor Authentication', - 'app:locale' => 'Default Language', - ]; - } -} diff --git a/app/Http/Requests/Admin/Settings/MailSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/MailSettingsFormRequest.php deleted file mode 100644 index 92a23272f..000000000 --- a/app/Http/Requests/Admin/Settings/MailSettingsFormRequest.php +++ /dev/null @@ -1,45 +0,0 @@ - 'required|string', - 'mail:port' => 'required|integer|between:1,65535', - 'mail:encryption' => ['present', Rule::in([null, 'tls', 'ssl'])], - 'mail:username' => 'nullable|string|max:255', - 'mail:password' => 'nullable|string|max:255', - 'mail:from:address' => 'required|string|email', - 'mail:from:name' => 'nullable|string|max:255', - ]; - } - - /** - * Override the default normalization function for this type of request - * as we need to accept empty values on the keys. - * - * @param array $only - * @return array - */ - public function normalize(array $only = null) - { - $keys = array_flip(array_keys($this->rules())); - - if (empty($this->input('mail:password'))) { - unset($keys['mail:password']); - } - - return $this->only(array_flip($keys)); - } -} diff --git a/app/Http/Requests/Admin/UserFormRequest.php b/app/Http/Requests/Admin/UserFormRequest.php deleted file mode 100644 index c6a358398..000000000 --- a/app/Http/Requests/Admin/UserFormRequest.php +++ /dev/null @@ -1,27 +0,0 @@ -method() === 'PATCH') { - $rules = collect(User::getUpdateRulesForId($this->route()->parameter('user')->id))->merge([ - 'ignore_connection_error' => ['sometimes', 'nullable', 'boolean'], - ]); - } - - return $rules->only([ - 'email', 'username', 'name_first', 'name_last', 'password', - 'language', 'ignore_connection_error', 'root_admin', - ])->toArray(); - } -} diff --git a/app/Http/Requests/Api/ApiRequest.php b/app/Http/Requests/Api/ApiRequest.php new file mode 100644 index 000000000..253a61691 --- /dev/null +++ b/app/Http/Requests/Api/ApiRequest.php @@ -0,0 +1,83 @@ +passesAuthorization()) { + $this->failedAuthorization(); + } + + $this->hasValidated = true; + } + + /* + * Determine if the request passes the authorization check as well + * as the exists check. + * + * @return bool + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + protected function passesAuthorization() + { + // If we have already validated we do not need to call this function + // again. This is needed to work around Laravel's normal auth validation + // that occurs after validating the request params since we are doing auth + // validation in the prepareForValidation() function. + if ($this->hasValidated) { + return true; + } + + if (!parent::passesAuthorization()) { + return false; + } + + // Only let the user know that a resource does not exist if they are + // authenticated to access the endpoint. This avoids exposing that + // an item exists (or does not exist) to the user until they can prove + // that they have permission to know about it. + if ($this->attributes->get('is_missing_model', false)) { + throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); + } + + return true; + } +} diff --git a/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php b/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php index 21da6bfb3..d062f7648 100644 --- a/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php +++ b/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php @@ -2,40 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Allocations; -use Pterodactyl\Models\Node; -use Pterodactyl\Models\Allocation; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class DeleteAllocationRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_ALLOCATIONS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Determine if the requested allocation exists and belongs to the node that - * is being passed in the URL. - * - * @return bool - */ - public function resourceExists(): bool - { - $node = $this->route()->parameter('node'); - $allocation = $this->route()->parameter('allocation'); - - if ($node instanceof Node && $node->exists) { - if ($allocation instanceof Allocation && $allocation->exists && $allocation->node_id === $node->id) { - return true; - } - } - - return false; - } } diff --git a/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php b/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php index 150bdb95e..2f536af60 100644 --- a/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php +++ b/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php @@ -2,32 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Allocations; -use Pterodactyl\Models\Node; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetAllocationsRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_ALLOCATIONS; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; - - /** - * Determine if the node that we are requesting the allocations - * for exists on the Panel. - * - * @return bool - */ - public function resourceExists(): bool - { - $node = $this->route()->parameter('node'); - - return $node instanceof Node && $node->exists; - } } diff --git a/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php b/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php index f795a114e..0474408ab 100644 --- a/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php +++ b/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php @@ -2,45 +2,37 @@ namespace Pterodactyl\Http\Requests\Api\Application\Allocations; -use Pterodactyl\Services\Acl\Api\AdminAcl; +use Illuminate\Support\Arr; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class StoreAllocationRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_ALLOCATIONS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * @return array - */ public function rules(): array { return [ 'ip' => 'required|string', - 'alias' => 'sometimes|nullable|string|max:255', + 'alias' => 'sometimes|nullable|string|max:191', 'ports' => 'required|array', 'ports.*' => 'string', ]; } /** - * @return array + * @param string|null $key + * @param string|array|null $default + * + * @return mixed */ - public function validated() + public function validated($key = null, $default = null) { $data = parent::validated(); - return [ + $response = [ 'allocation_ip' => $data['ip'], 'allocation_ports' => $data['ports'], 'allocation_alias' => $data['alias'] ?? null, ]; + + return is_null($key) ? $response : Arr::get($response, $key, $default); } } diff --git a/app/Http/Requests/Api/Application/ApplicationApiRequest.php b/app/Http/Requests/Api/Application/ApplicationApiRequest.php index ca5f40dd0..70f6cb219 100644 --- a/app/Http/Requests/Api/Application/ApplicationApiRequest.php +++ b/app/Http/Requests/Api/Application/ApplicationApiRequest.php @@ -2,156 +2,16 @@ namespace Pterodactyl\Http\Requests\Api\Application; -use Pterodactyl\Models\ApiKey; -use Pterodactyl\Services\Acl\Api\AdminAcl; -use Illuminate\Foundation\Http\FormRequest; -use Pterodactyl\Exceptions\PterodactylException; -use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\Routing\Exception\InvalidParameterException; +use Pterodactyl\Http\Requests\Api\ApiRequest; -abstract class ApplicationApiRequest extends FormRequest +abstract class ApplicationApiRequest extends ApiRequest { /** - * Tracks if the request has been validated internally or not to avoid - * making duplicate validation calls. - * - * @var bool - */ - private $hasValidated = false; - - /** - * The resource that should be checked when performing the authorization - * function for this request. - * - * @var string|null - */ - protected $resource; - - /** - * The permission level that a given API key should have for accessing - * the defined $resource during the request cycle. - * - * @var int - */ - protected $permission = AdminAcl::NONE; - - /** - * Determine if the current user is authorized to perform - * the requested action against the API. - * - * @return bool - * - * @throws \Pterodactyl\Exceptions\PterodactylException + * This will eventually be replaced with per-request permissions checking + * on the API key and for the user. */ public function authorize(): bool { - if (is_null($this->resource)) { - throw new PterodactylException('An ACL resource must be defined on API requests.'); - } - - return AdminAcl::check($this->key(), $this->resource, $this->permission); - } - - /** - * Determine if the requested resource exists on the server. - * - * @return bool - */ - public function resourceExists(): bool - { - return true; - } - - /** - * Default set of rules to apply to API requests. - * - * @return array - */ - public function rules(): array - { - return []; - } - - /** - * Return the API key being used for the request. - * - * @return \Pterodactyl\Models\ApiKey - */ - public function key(): ApiKey - { - return $this->attributes->get('api_key'); - } - - /** - * Grab a model from the route parameters. If no model is found in the - * binding mappings an exception will be thrown. - * - * @param string $model - * @return mixed - * - * @throws \Symfony\Component\Routing\Exception\InvalidParameterException - */ - public function getModel(string $model) - { - $parameterKey = array_get(array_flip(ApiSubstituteBindings::getMappings()), $model); - - if (is_null($parameterKey)) { - throw new InvalidParameterException; - } - - return $this->route()->parameter($parameterKey); - } - - /** - * Validate that the resource exists and can be accessed prior to booting - * the validator and attempting to use the data. - * - * @throws \Illuminate\Auth\Access\AuthorizationException - */ - protected function prepareForValidation() - { - if (! $this->passesAuthorization()) { - $this->failedAuthorization(); - } - - $this->hasValidated = true; - } - - /* - * Determine if the request passes the authorization check as well - * as the exists check. - * - * @return bool - * - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - */ - - /** - * @return bool - */ - protected function passesAuthorization() - { - // If we have already validated we do not need to call this function - // again. This is needed to work around Laravel's normal auth validation - // that occurs after validating the request params since we are doing auth - // validation in the prepareForValidation() function. - if ($this->hasValidated) { - return true; - } - - if (! parent::passesAuthorization()) { - return false; - } - - // Only let the user know that a resource does not exist if they are - // authenticated to access the endpoint. This avoids exposing that - // an item exists (or does not exist) to the user until they can prove - // that they have permission to know about it. - if ($this->attributes->get('is_missing_model', false) || ! $this->resourceExists()) { - throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); - } - - return true; + return $this->user()->root_admin; } } diff --git a/app/Http/Requests/Api/Application/Databases/DeleteDatabaseRequest.php b/app/Http/Requests/Api/Application/Databases/DeleteDatabaseRequest.php new file mode 100644 index 000000000..cde56da1c --- /dev/null +++ b/app/Http/Requests/Api/Application/Databases/DeleteDatabaseRequest.php @@ -0,0 +1,9 @@ +route()->parameter('databaseHost')); + } +} diff --git a/app/Http/Requests/Api/Application/Eggs/DeleteEggRequest.php b/app/Http/Requests/Api/Application/Eggs/DeleteEggRequest.php new file mode 100644 index 000000000..154f06efd --- /dev/null +++ b/app/Http/Requests/Api/Application/Eggs/DeleteEggRequest.php @@ -0,0 +1,16 @@ +route()->parameter('egg'); + + return $egg instanceof Egg && $egg->exists; + } +} diff --git a/app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php b/app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php new file mode 100644 index 000000000..63893df54 --- /dev/null +++ b/app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php @@ -0,0 +1,9 @@ + 'required|bail|numeric|exists:nests,id', + 'name' => 'required|string|max:191', + 'description' => 'sometimes|string|nullable', + 'features' => 'sometimes|array', + 'docker_images' => 'required|array|min:1', + 'docker_images.*' => 'required|string', + 'file_denylist' => 'sometimes|array|nullable', + 'file_denylist.*' => 'sometimes|string', + 'config_files' => 'required|nullable|json', + 'config_startup' => 'required|nullable|json', + 'config_stop' => 'required|nullable|string|max:191', +// 'config_from' => 'sometimes|nullable|numeric|exists:eggs,id', + 'startup' => 'required|string', + 'script_container' => 'sometimes|string', + 'script_entry' => 'sometimes|string', + 'script_install' => 'sometimes|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Eggs/UpdateEggRequest.php b/app/Http/Requests/Api/Application/Eggs/UpdateEggRequest.php new file mode 100644 index 000000000..090f5b51c --- /dev/null +++ b/app/Http/Requests/Api/Application/Eggs/UpdateEggRequest.php @@ -0,0 +1,28 @@ + 'sometimes|numeric|exists:nests,id', + 'name' => 'sometimes|string|max:191', + 'description' => 'sometimes|string|nullable', + 'features' => 'sometimes|array', + 'docker_images' => 'sometimes|array|min:1', + 'docker_images.*' => 'sometimes|string', + 'file_denylist' => 'sometimes|array|nullable', + 'file_denylist.*' => 'sometimes|string', + 'config_files' => 'sometimes|nullable|json', + 'config_startup' => 'sometimes|nullable|json', + 'config_stop' => 'sometimes|nullable|string|max:191', +// 'config_from' => 'sometimes|nullable|numeric|exists:eggs,id', + 'startup' => 'sometimes|string', + 'script_container' => 'sometimes|string', + 'script_entry' => 'sometimes|string', + 'script_install' => 'sometimes|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Eggs/Variables/StoreEggVariableRequest.php b/app/Http/Requests/Api/Application/Eggs/Variables/StoreEggVariableRequest.php new file mode 100644 index 000000000..9c674a9d8 --- /dev/null +++ b/app/Http/Requests/Api/Application/Eggs/Variables/StoreEggVariableRequest.php @@ -0,0 +1,22 @@ + 'required|string|min:1|max:191', + 'description' => 'sometimes|string|nullable', + 'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES, + 'default_value' => 'present', + 'user_viewable' => 'required|boolean', + 'user_editable' => 'required|boolean', + 'rules' => 'bail|required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Eggs/Variables/UpdateEggVariablesRequest.php b/app/Http/Requests/Api/Application/Eggs/Variables/UpdateEggVariablesRequest.php new file mode 100644 index 000000000..c15de2ce3 --- /dev/null +++ b/app/Http/Requests/Api/Application/Eggs/Variables/UpdateEggVariablesRequest.php @@ -0,0 +1,24 @@ + 'array', + '*.id' => 'required|integer', + '*.name' => 'sometimes|string|min:1|max:191', + '*.description' => 'sometimes|string|nullable', + '*.env_variable' => 'sometimes|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES, + '*.default_value' => 'sometimes|present', + '*.user_viewable' => 'sometimes|boolean', + '*.user_editable' => 'sometimes|boolean', + '*.rules' => 'sometimes|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php b/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php index d1863eea7..eb2cffd34 100644 --- a/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php +++ b/app/Http/Requests/Api/Application/Locations/DeleteLocationRequest.php @@ -2,31 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Locations; -use Pterodactyl\Models\Location; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class DeleteLocationRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_LOCATIONS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Determine if the requested location exists on the Panel. - * - * @return bool - */ - public function resourceExists(): bool - { - $location = $this->route()->parameter('location'); - - return $location instanceof Location && $location->exists; - } } diff --git a/app/Http/Requests/Api/Application/Locations/GetLocationRequest.php b/app/Http/Requests/Api/Application/Locations/GetLocationRequest.php index d49fb266f..dea82db33 100644 --- a/app/Http/Requests/Api/Application/Locations/GetLocationRequest.php +++ b/app/Http/Requests/Api/Application/Locations/GetLocationRequest.php @@ -2,19 +2,6 @@ namespace Pterodactyl\Http\Requests\Api\Application\Locations; -use Pterodactyl\Models\Location; - class GetLocationRequest extends GetLocationsRequest { - /** - * Determine if the requested location exists on the Panel. - * - * @return bool - */ - public function resourceExists(): bool - { - $location = $this->route()->parameter('location'); - - return $location instanceof Location && $location->exists; - } } diff --git a/app/Http/Requests/Api/Application/Locations/GetLocationsRequest.php b/app/Http/Requests/Api/Application/Locations/GetLocationsRequest.php index 5edf00462..dea300b91 100644 --- a/app/Http/Requests/Api/Application/Locations/GetLocationsRequest.php +++ b/app/Http/Requests/Api/Application/Locations/GetLocationsRequest.php @@ -2,18 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Locations; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetLocationsRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_LOCATIONS; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Locations/StoreLocationRequest.php b/app/Http/Requests/Api/Application/Locations/StoreLocationRequest.php index 761c669d6..9b403fa10 100644 --- a/app/Http/Requests/Api/Application/Locations/StoreLocationRequest.php +++ b/app/Http/Requests/Api/Application/Locations/StoreLocationRequest.php @@ -3,40 +3,19 @@ namespace Pterodactyl\Http\Requests\Api\Application\Locations; use Pterodactyl\Models\Location; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class StoreLocationRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_LOCATIONS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Rules to validate the request against. - * - * @return array - */ public function rules(): array { - return collect(Location::getCreateRules())->only([ + return collect(Location::getRules())->only([ 'long', 'short', ])->toArray(); } - /** - * Rename fields to be more clear in error messages. - * - * @return array - */ - public function attributes() + public function attributes(): array { return [ 'long' => 'Location Description', diff --git a/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php b/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php index 2d83ab08f..f5d79deb2 100644 --- a/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php +++ b/app/Http/Requests/Api/Application/Locations/UpdateLocationRequest.php @@ -6,28 +6,11 @@ use Pterodactyl\Models\Location; class UpdateLocationRequest extends StoreLocationRequest { - /** - * Determine if the requested location exists on the Panel. - * - * @return bool - */ - public function resourceExists(): bool - { - $location = $this->route()->parameter('location'); - - return $location instanceof Location && $location->exists; - } - - /** - * Rules to validate this request against. - * - * @return array - */ public function rules(): array { - $locationId = $this->route()->parameter('location')->id; + $locationId = $this->route()->parameter('location'); - return collect(Location::getUpdateRulesForId($locationId))->only([ + return collect(Location::getRulesForUpdate($locationId))->only([ 'short', 'long', ])->toArray(); diff --git a/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php b/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php new file mode 100644 index 000000000..1325510f2 --- /dev/null +++ b/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php @@ -0,0 +1,9 @@ + 'required|exists:eggs,id']; + } +} diff --git a/app/Http/Requests/Api/Application/Mounts/MountNodesRequest.php b/app/Http/Requests/Api/Application/Mounts/MountNodesRequest.php new file mode 100644 index 000000000..4810591a8 --- /dev/null +++ b/app/Http/Requests/Api/Application/Mounts/MountNodesRequest.php @@ -0,0 +1,13 @@ + 'required|exists:nodes,id']; + } +} diff --git a/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php b/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php new file mode 100644 index 000000000..ba678d186 --- /dev/null +++ b/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php @@ -0,0 +1,14 @@ +route()->parameter('mount')); + } +} diff --git a/app/Http/Requests/Api/Application/Nests/DeleteNestRequest.php b/app/Http/Requests/Api/Application/Nests/DeleteNestRequest.php new file mode 100644 index 000000000..8d505c93e --- /dev/null +++ b/app/Http/Requests/Api/Application/Nests/DeleteNestRequest.php @@ -0,0 +1,9 @@ +getModel(Nest::class)->id === $this->getModel(Egg::class)->nest_id; - } -} diff --git a/app/Http/Requests/Api/Application/Nests/Eggs/GetEggsRequest.php b/app/Http/Requests/Api/Application/Nests/Eggs/GetEggsRequest.php deleted file mode 100644 index a6aadf904..000000000 --- a/app/Http/Requests/Api/Application/Nests/Eggs/GetEggsRequest.php +++ /dev/null @@ -1,19 +0,0 @@ -route()->parameter('nest')); + } +} diff --git a/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php index 77dd24eac..4bd5159d0 100644 --- a/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php @@ -2,32 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Nodes; -use Pterodactyl\Models\Node; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class DeleteNodeRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_NODES; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Determine if the node being requested for editing exists - * on the Panel before validating the data. - * - * @return bool - */ - public function resourceExists(): bool - { - $node = $this->route()->parameter('node'); - - return $node instanceof Node && $node->exists; - } } diff --git a/app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php b/app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php new file mode 100644 index 000000000..fd077ecd5 --- /dev/null +++ b/app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php @@ -0,0 +1,17 @@ + 'integer', + 'memory' => 'required|integer|min:0', + 'disk' => 'required|integer|min:0', + 'location_ids' => 'array', + 'location_ids.*' => 'integer', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php index fbf957edd..6d231bc97 100644 --- a/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/GetNodeRequest.php @@ -2,19 +2,6 @@ namespace Pterodactyl\Http\Requests\Api\Application\Nodes; -use Pterodactyl\Models\Node; - class GetNodeRequest extends GetNodesRequest { - /** - * Determine if the requested node exists on the Panel. - * - * @return bool - */ - public function resourceExists(): bool - { - $node = $this->route()->parameter('node'); - - return $node instanceof Node && $node->exists; - } } diff --git a/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php b/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php index fc5f5a38e..ac6191ea5 100644 --- a/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php @@ -2,18 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Nodes; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetNodesRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_NODES; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php index 37dd32585..6d7629302 100644 --- a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php @@ -2,58 +2,48 @@ namespace Pterodactyl\Http\Requests\Api\Application\Nodes; +use Illuminate\Support\Arr; use Pterodactyl\Models\Node; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class StoreNodeRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_NODES; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - /** * Validation rules to apply to this request. - * - * @param null|array $rules - * @return array */ public function rules(array $rules = null): array { - return collect($rules ?? Node::getCreateRules())->only([ - 'public', + return collect($rules ?? Node::getRules())->only([ 'name', + 'description', 'location_id', + 'database_host_id', 'fqdn', 'scheme', 'behind_proxy', + 'public', + + 'listen_port_http', + 'public_port_http', + 'listen_port_sftp', + 'public_port_sftp', + 'memory', 'memory_overallocate', 'disk', 'disk_overallocate', - 'upload_size', - 'daemonListen', - 'daemonSFTP', - 'daemonBase', - ])->mapWithKeys(function ($value, $key) { - $key = ($key === 'daemonSFTP') ? 'daemonSftp' : $key; + 'upload_size', + 'daemon_base', + ])->mapWithKeys(function ($value, $key) { return [snake_case($key) => $value]; })->toArray(); } /** * Fields to rename for clarity in the API response. - * - * @return array */ - public function attributes() + public function attributes(): array { return [ 'daemon_base' => 'Daemon Base Path', @@ -66,17 +56,15 @@ class StoreNodeRequest extends ApplicationApiRequest /** * Change the formatting of some data keys in the validated response data * to match what the application expects in the services. - * - * @return array */ - public function validated() + public function validated($key = null, $default = null): array { $response = parent::validated(); - $response['daemonListen'] = $response['daemon_listen']; - $response['daemonSFTP'] = $response['daemon_sftp']; - $response['daemonBase'] = $response['daemon_base'] ?? (new Node)->getAttribute('daemonBase'); + $response['daemon_base'] = $response['daemon_base'] ?? Node::DEFAULT_DAEMON_BASE; - unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']); + if (!is_null($key)) { + return Arr::get($response, $key, $default); + } return $response; } diff --git a/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php index ffba39e6d..ae8aab357 100644 --- a/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php @@ -6,17 +6,8 @@ use Pterodactyl\Models\Node; class UpdateNodeRequest extends StoreNodeRequest { - /** - * Apply validation rules to this request. Uses the parent class rules() - * function but passes in the rules for updating rather than creating. - * - * @param array|null $rules - * @return array - */ public function rules(array $rules = null): array { - $nodeId = $this->getModel(Node::class)->id; - - return parent::rules(Node::getUpdateRulesForId($nodeId)); + return parent::rules($rules ?? Node::getRulesForUpdate($this->route()->parameter('node'))); } } diff --git a/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php b/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php new file mode 100644 index 000000000..5f6cd34b5 --- /dev/null +++ b/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php @@ -0,0 +1,9 @@ +route()->parameter('role')); + } +} diff --git a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php index e398a5bbf..dd1dd2fd1 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php @@ -2,31 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetServerDatabaseRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVER_DATABASES; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; - - /** - * Determine if the requested server database exists. - * - * @return bool - */ - public function resourceExists(): bool - { - $server = $this->route()->parameter('server'); - $database = $this->route()->parameter('database'); - - return $database->server_id === $server->id; - } } diff --git a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php index 3e6cfc6fe..74f942278 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php @@ -2,18 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetServerDatabasesRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVER_DATABASES; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Servers/Databases/ServerDatabaseWriteRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/ServerDatabaseWriteRequest.php index 917a5313f..827d68c55 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/ServerDatabaseWriteRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/ServerDatabaseWriteRequest.php @@ -2,12 +2,6 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases; -use Pterodactyl\Services\Acl\Api\AdminAcl; - class ServerDatabaseWriteRequest extends GetServerDatabasesRequest { - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php index c2dbfe14a..706af5de6 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php @@ -2,38 +2,29 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases; +use Illuminate\Support\Arr; +use Webmozart\Assert\Assert; +use Pterodactyl\Models\Server; use Illuminate\Validation\Rule; use Illuminate\Database\Query\Builder; -use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Services\Databases\DatabaseManagementService; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class StoreServerDatabaseRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVER_DATABASES; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Validation rules for database creation. - * - * @return array - */ public function rules(): array { + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->route()->parameter('server'); + return [ 'database' => [ 'required', - 'string', + 'alpha_dash', 'min:1', - 'max:24', - Rule::unique('databases')->where(function (Builder $query) { - $query->where('database_host_id', $this->input('host') ?? 0); + 'max:48', + Rule::unique('databases')->where(function (Builder $query) use ($server) { + $query->where('server_id', $server->id)->where('database', $this->databaseName()); }), ], 'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/', @@ -42,25 +33,23 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest } /** - * Return data formatted in the correct format for the service to consume. + * @param string|null $key + * @param string|array|null $default * - * @return array + * @return mixed */ - public function validated() + public function validated($key = null, $default = null) { - return [ + $data = [ 'database' => $this->input('database'), 'remote' => $this->input('remote'), 'database_host_id' => $this->input('host'), ]; + + return is_null($key) ? $data : Arr::get($data, $key, $default); } - /** - * Format error messages in a more understandable format for API output. - * - * @return array - */ - public function attributes() + public function attributes(): array { return [ 'host' => 'Database Host Server ID', @@ -68,4 +57,13 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest 'database' => 'Database Name', ]; } + + public function databaseName(): string + { + $server = $this->route()->parameter('server'); + + Assert::isInstanceOf($server, Server::class); + + return DatabaseManagementService::generateUniqueDatabaseName($this->input('database'), $server->id); + } } diff --git a/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php b/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php index f783dc715..790f55798 100644 --- a/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php @@ -2,56 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers; -use Pterodactyl\Models\Server; -use Pterodactyl\Services\Acl\Api\AdminAcl; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetExternalServerRequest extends ApplicationApiRequest { - /** - * @var \Pterodactyl\Models\Server - */ - private $serverModel; - - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVERS; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; - - /** - * Determine if the requested external user exists. - * - * @return bool - */ - public function resourceExists(): bool - { - $repository = $this->container->make(ServerRepositoryInterface::class); - - try { - $this->serverModel = $repository->findFirstWhere([ - ['external_id', '=', $this->route()->parameter('external_id')], - ]); - } catch (RecordNotFoundException $exception) { - return false; - } - - return true; - } - - /** - * Return the server model for the requested external server. - * - * @return \Pterodactyl\Models\Server - */ - public function getServerModel(): Server - { - return $this->serverModel; - } } diff --git a/app/Http/Requests/Api/Application/Servers/GetServerRequest.php b/app/Http/Requests/Api/Application/Servers/GetServerRequest.php index 82d12687c..2f4f417cd 100644 --- a/app/Http/Requests/Api/Application/Servers/GetServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/GetServerRequest.php @@ -2,18 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetServerRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVERS; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Servers/GetServersRequest.php b/app/Http/Requests/Api/Application/Servers/GetServersRequest.php index 8a0a30535..ece977662 100644 --- a/app/Http/Requests/Api/Application/Servers/GetServersRequest.php +++ b/app/Http/Requests/Api/Application/Servers/GetServersRequest.php @@ -4,9 +4,6 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers; class GetServersRequest extends GetServerRequest { - /** - * @return array - */ public function rules(): array { return [ diff --git a/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php index 07c201336..e8d01a115 100644 --- a/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php +++ b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php @@ -2,18 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class ServerWriteRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVERS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index 4378474b0..4f8bd872c 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -2,159 +2,88 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers; +use Illuminate\Support\Arr; use Pterodactyl\Models\Server; -use Illuminate\Validation\Rule; -use Pterodactyl\Services\Acl\Api\AdminAcl; -use Illuminate\Contracts\Validation\Validator; -use Pterodactyl\Models\Objects\DeploymentObject; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class StoreServerRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVERS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Rules to be applied to this request. - * - * @return array - */ public function rules(): array { - $rules = Server::getCreateRules(); + $rules = Server::getRules(); return [ 'external_id' => $rules['external_id'], 'name' => $rules['name'], 'description' => array_merge(['nullable'], $rules['description']), - 'user' => $rules['owner_id'], - 'egg' => $rules['egg_id'], - 'pack' => $rules['pack_id'], - 'docker_image' => $rules['image'], - 'startup' => $rules['startup'], - 'environment' => 'present|array', - 'skip_scripts' => 'sometimes|boolean', - 'oom_disabled' => 'sometimes|boolean', + 'owner_id' => $rules['owner_id'], + 'node_id' => $rules['node_id'], - // Resource limitations 'limits' => 'required|array', 'limits.memory' => $rules['memory'], 'limits.swap' => $rules['swap'], 'limits.disk' => $rules['disk'], 'limits.io' => $rules['io'], + 'limits.threads' => $rules['threads'], 'limits.cpu' => $rules['cpu'], + 'limits.oom_killer' => 'required|boolean', - // Application Resource Limits 'feature_limits' => 'required|array', - 'feature_limits.databases' => $rules['database_limit'], 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], + 'feature_limits.databases' => $rules['database_limit'], - // Placeholders for rules added in withValidator() function. - 'allocation.default' => '', - 'allocation.additional.*' => '', + 'allocation.default' => 'required|bail|integer|exists:allocations,id', + 'allocation.additional.*' => 'integer|exists:allocations,id', - // Automatic deployment rules - 'deploy' => 'sometimes|required|array', - 'deploy.locations' => 'array', - 'deploy.locations.*' => 'integer|min:1', - 'deploy.dedicated_ip' => 'required_with:deploy,boolean', - 'deploy.port_range' => 'array', - 'deploy.port_range.*' => 'string', - - 'start_on_completion' => 'sometimes|boolean', + 'startup' => $rules['startup'], + 'environment' => 'present|array', + 'egg_id' => $rules['egg_id'], + 'image' => $rules['image'], + 'skip_scripts' => 'present|boolean', ]; } /** - * Normalize the data into a format that can be consumed by the service. + * @param string|null $key + * @param string|array|null $default * * @return array */ - public function validated() + public function validated($key = null, $default = null) { $data = parent::validated(); - return [ + $response = [ 'external_id' => array_get($data, 'external_id'), 'name' => array_get($data, 'name'), 'description' => array_get($data, 'description'), - 'owner_id' => array_get($data, 'user'), - 'egg_id' => array_get($data, 'egg'), - 'pack_id' => array_get($data, 'pack'), - 'image' => array_get($data, 'docker_image'), - 'startup' => array_get($data, 'startup'), - 'environment' => array_get($data, 'environment'), + 'owner_id' => array_get($data, 'owner_id'), + 'node_id' => array_get($data, 'node_id'), + 'memory' => array_get($data, 'limits.memory'), 'swap' => array_get($data, 'limits.swap'), 'disk' => array_get($data, 'limits.disk'), 'io' => array_get($data, 'limits.io'), + 'threads' => array_get($data, 'limits.threads'), 'cpu' => array_get($data, 'limits.cpu'), - 'skip_scripts' => array_get($data, 'skip_scripts', false), + 'oom_killer' => array_get($data, 'limits.oom_killer'), + + 'allocation_limit' => array_get($data, 'feature_limits.allocations'), + 'backup_limit' => array_get($data, 'feature_limits.backups'), + 'database_limit' => array_get($data, 'feature_limits.databases'), + 'allocation_id' => array_get($data, 'allocation.default'), 'allocation_additional' => array_get($data, 'allocation.additional'), + + 'startup' => array_get($data, 'startup'), + 'environment' => array_get($data, 'environment'), + 'egg_id' => array_get($data, 'egg_id'), + 'image' => array_get($data, 'image'), + 'skip_scripts' => array_get($data, 'skip_scripts'), 'start_on_completion' => array_get($data, 'start_on_completion', false), - 'database_limit' => array_get($data, 'feature_limits.databases'), - 'allocation_limit' => array_get($data, 'feature_limits.allocations'), ]; - } - /* - * Run validation after the rules above have been applied. - * - * @param \Illuminate\Contracts\Validation\Validator $validator - */ - public function withValidator(Validator $validator) - { - $validator->sometimes('allocation.default', [ - 'required', 'integer', 'bail', - Rule::exists('allocations', 'id')->where(function ($query) { - $query->whereNull('server_id'); - }), - ], function ($input) { - return ! ($input->deploy); - }); - - $validator->sometimes('allocation.additional.*', [ - 'integer', - Rule::exists('allocations', 'id')->where(function ($query) { - $query->whereNull('server_id'); - }), - ], function ($input) { - return ! ($input->deploy); - }); - - $validator->sometimes('deploy.locations', 'present', function ($input) { - return $input->deploy; - }); - - $validator->sometimes('deploy.port_range', 'present', function ($input) { - return $input->deploy; - }); - } - - /** - * Return a deployment object that can be passed to the server creation service. - * - * @return \Pterodactyl\Models\Objects\DeploymentObject|null - */ - public function getDeploymentObject() - { - if (is_null($this->input('deploy'))) { - return null; - } - - $object = new DeploymentObject; - $object->setDedicated($this->input('deploy.dedicated_ip', false)); - $object->setLocations($this->input('deploy.locations', [])); - $object->setPorts($this->input('deploy.port_range', [])); - - return $object; + return is_null($key) ? $response : Arr::get($response, $key, $default); } } diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php deleted file mode 100644 index 0238cdcfe..000000000 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php +++ /dev/null @@ -1,119 +0,0 @@ -getModel(Server::class)->id); - - return [ - 'allocation' => $rules['allocation_id'], - 'oom_disabled' => $rules['oom_disabled'], - - 'limits' => 'sometimes|array', - 'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true), - 'limits.swap' => $this->requiredToOptional('swap', $rules['swap'], true), - 'limits.io' => $this->requiredToOptional('io', $rules['io'], true), - 'limits.cpu' => $this->requiredToOptional('cpu', $rules['cpu'], true), - 'limits.disk' => $this->requiredToOptional('disk', $rules['disk'], true), - - // Legacy rules to maintain backwards compatable API support without requiring - // a major version bump. - // - // @see https://github.com/pterodactyl/panel/issues/1500 - 'memory' => $this->requiredToOptional('memory', $rules['memory']), - 'swap' => $this->requiredToOptional('swap', $rules['swap']), - 'io' => $this->requiredToOptional('io', $rules['io']), - 'cpu' => $this->requiredToOptional('cpu', $rules['cpu']), - 'disk' => $this->requiredToOptional('disk', $rules['disk']), - - 'add_allocations' => 'bail|array', - 'add_allocations.*' => 'integer', - 'remove_allocations' => 'bail|array', - 'remove_allocations.*' => 'integer', - - 'feature_limits' => 'required|array', - 'feature_limits.databases' => $rules['database_limit'], - 'feature_limits.allocations' => $rules['allocation_limit'], - ]; - } - - /** - * Convert the allocation field into the expected format for the service handler. - * - * @return array - */ - public function validated() - { - $data = parent::validated(); - - $data['allocation_id'] = $data['allocation']; - $data['database_limit'] = $data['feature_limits']['databases']; - $data['allocation_limit'] = $data['feature_limits']['allocations']; - unset($data['allocation'], $data['feature_limits']); - - // Adjust the limits field to match what is expected by the model. - if (! empty($data['limits'])) { - foreach ($data['limits'] as $key => $value) { - $data[$key] = $value; - } - - unset($data['limits']); - } - - return $data; - } - - /** - * Custom attributes to use in error message responses. - * - * @return array - */ - public function attributes() - { - return [ - 'add_allocations' => 'allocations to add', - 'remove_allocations' => 'allocations to remove', - 'add_allocations.*' => 'allocation to add', - 'remove_allocations.*' => 'allocation to remove', - 'feature_limits.databases' => 'Database Limit', - 'feature_limits.allocations' => 'Allocation Limit', - ]; - } - - /** - * Converts existing rules for certain limits into a format that maintains backwards - * compatability with the old API endpoint while also supporting a more correct API - * call. - * - * @param string $field - * @param array $rules - * @param bool $limits - * @return array - * - * @see https://github.com/pterodactyl/panel/issues/1500 - */ - protected function requiredToOptional(string $field, array $rules, bool $limits = false) - { - if (! in_array('required', $rules)) { - return $rules; - } - - return (new Collection($rules)) - ->filter(function ($value) { - return $value !== 'required'; - }) - ->prepend($limits ? 'required_with:limits' : 'required_without:limits') - ->toArray(); - } -} diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php deleted file mode 100644 index aa585bad6..000000000 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php +++ /dev/null @@ -1,55 +0,0 @@ -getModel(Server::class)->id); - - return [ - 'external_id' => $rules['external_id'], - 'name' => $rules['name'], - 'user' => $rules['owner_id'], - 'description' => array_merge(['nullable'], $rules['description']), - ]; - } - - /** - * Convert the posted data into the correct format that is expected - * by the application. - * - * @return array - */ - public function validated(): array - { - return [ - 'external_id' => $this->input('external_id'), - 'name' => $this->input('name'), - 'owner_id' => $this->input('user'), - 'description' => $this->input('description'), - ]; - } - - /** - * Rename some of the attributes in error messages to clarify the field - * being discussed. - * - * @return array - */ - public function attributes(): array - { - return [ - 'user' => 'User ID', - 'name' => 'Server Name', - ]; - } -} diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php new file mode 100644 index 000000000..faa8ddb1c --- /dev/null +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php @@ -0,0 +1,77 @@ + $rules['external_id'], + 'name' => $rules['name'], + 'description' => array_merge(['nullable'], $rules['description']), + 'owner_id' => $rules['owner_id'], + + 'limits' => 'sometimes|array', + 'limits.memory' => $rules['memory'], + 'limits.swap' => $rules['swap'], + 'limits.disk' => $rules['disk'], + 'limits.io' => $rules['io'], + 'limits.threads' => $rules['threads'], + 'limits.cpu' => $rules['cpu'], + 'limits.oom_killer' => 'sometimes|boolean', + + 'feature_limits' => 'required|array', + 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], + 'feature_limits.databases' => $rules['database_limit'], + + 'allocation_id' => 'bail|exists:allocations,id', + 'add_allocations' => 'bail|array', + 'add_allocations.*' => 'integer', + 'remove_allocations' => 'bail|array', + 'remove_allocations.*' => 'integer', + ]; + } + + /** + * @param string|null $key + * @param string|array|null $default + * + * @return mixed + */ + public function validated($key = null, $default = null) + { + $data = parent::validated(); + $response = [ + 'external_id' => array_get($data, 'external_id'), + 'name' => array_get($data, 'name'), + 'description' => array_get($data, 'description'), + 'owner_id' => array_get($data, 'owner_id'), + + 'memory' => array_get($data, 'limits.memory'), + 'swap' => array_get($data, 'limits.swap'), + 'disk' => array_get($data, 'limits.disk'), + 'io' => array_get($data, 'limits.io'), + 'threads' => array_get($data, 'limits.threads'), + 'cpu' => array_get($data, 'limits.cpu'), + 'oom_killer' => array_get($data, 'limits.oom_killer'), + + 'allocation_limit' => array_get($data, 'feature_limits.allocations'), + 'backup_limit' => array_get($data, 'feature_limits.backups'), + 'database_limit' => array_get($data, 'feature_limits.databases'), + + 'allocation_id' => array_get($data, 'allocation_id'), + 'add_allocations' => array_get($data, 'add_allocations'), + 'remove_allocations' => array_get($data, 'remove_allocations'), + ]; + + return is_null($key) ? $response : Arr::get($response, $key, $default); + } +} diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php index acb6043c1..fb16ffdac 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php @@ -3,53 +3,20 @@ namespace Pterodactyl\Http\Requests\Api\Application\Servers; use Pterodactyl\Models\Server; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class UpdateServerStartupRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_SERVERS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Validation rules to run the input against. - * - * @return array - */ public function rules(): array { - $data = Server::getUpdateRulesForId($this->getModel(Server::class)->id); + $rules = Server::getRulesForUpdate($this->route()->parameter('server')); return [ - 'startup' => $data['startup'], + 'startup' => $rules['startup'], 'environment' => 'present|array', - 'egg' => $data['egg_id'], - 'pack' => $data['pack_id'], - 'image' => $data['image'], + 'egg_id' => $rules['egg_id'], + 'image' => $rules['image'], 'skip_scripts' => 'present|boolean', ]; } - - /** - * Return the validated data in a format that is expected by the service. - * - * @return array - */ - public function validated() - { - $data = parent::validated(); - - return collect($data)->only(['startup', 'environment', 'skip_scripts'])->merge([ - 'egg_id' => array_get($data, 'egg'), - 'pack_id' => array_get($data, 'pack'), - 'docker_image' => array_get($data, 'image'), - ])->toArray(); - } } diff --git a/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php b/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php index 571b29c63..a2e3841fb 100644 --- a/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php +++ b/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php @@ -2,31 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Users; -use Pterodactyl\Models\User; -use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class DeleteUserRequest extends ApplicationApiRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_USERS; - - /** - * @var int - */ - protected $permission = AdminAcl::WRITE; - - /** - * Determine if the requested user exists on the Panel. - * - * @return bool - */ - public function resourceExists(): bool - { - $user = $this->route()->parameter('user'); - - return $user instanceof User && $user->exists; - } } diff --git a/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php b/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php index b1f779183..b26ef7661 100644 --- a/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php +++ b/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php @@ -2,55 +2,8 @@ namespace Pterodactyl\Http\Requests\Api\Application\Users; -use Pterodactyl\Models\User; -use Pterodactyl\Services\Acl\Api\AdminAcl; -use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; class GetExternalUserRequest extends ApplicationApiRequest { - /** - * @var User - */ - private $userModel; - - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_USERS; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; - - /** - * Determine if the requested external user exists. - * - * @return bool - */ - public function resourceExists(): bool - { - $repository = $this->container->make(UserRepositoryInterface::class); - - try { - $this->userModel = $repository->findFirstWhere([ - ['external_id', '=', $this->route()->parameter('external_id')], - ]); - } catch (RecordNotFoundException $exception) { - return false; - } - - return true; - } - - /** - * Return the user model for the requested external user. - * @return \Pterodactyl\Models\User - */ - public function getUserModel(): User - { - return $this->userModel; - } } diff --git a/app/Http/Requests/Api/Application/Users/GetUserRequest.php b/app/Http/Requests/Api/Application/Users/GetUserRequest.php new file mode 100644 index 000000000..4e16088a5 --- /dev/null +++ b/app/Http/Requests/Api/Application/Users/GetUserRequest.php @@ -0,0 +1,7 @@ +only([ + return collect($rules)->only([ 'external_id', 'email', 'username', 'password', - 'language', + 'admin_role_id', 'root_admin', ])->toArray(); - - $response['first_name'] = $rules['name_first']; - $response['last_name'] = $rules['name_last']; - - return $response; - } - - /** - * @return array - */ - public function validated() - { - $data = parent::validated(); - - $data['name_first'] = $data['first_name']; - $data['name_last'] = $data['last_name']; - - unset($data['first_name'], $data['last_name']); - - return $data; - } - - /** - * Rename some fields to be more user friendly. - * - * @return array - */ - public function attributes() - { - return [ - 'external_id' => 'Third Party Identifier', - 'name_first' => 'First Name', - 'name_last' => 'Last Name', - 'root_admin' => 'Root Administrator Status', - ]; } } diff --git a/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php b/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php index 929a77f32..3d7d9f75c 100644 --- a/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php +++ b/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php @@ -6,16 +6,8 @@ use Pterodactyl\Models\User; class UpdateUserRequest extends StoreUserRequest { - /** - * Return the validation rules for this request. - * - * @param array|null $rules - * @return array - */ public function rules(array $rules = null): array { - $userId = $this->getModel(User::class)->id; - - return parent::rules(User::getUpdateRulesForId($userId)); + return parent::rules($rules ?? User::getRulesForUpdate($this->route()->parameter('user'))); } } diff --git a/app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php b/app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php new file mode 100644 index 000000000..efa1cc3cd --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php @@ -0,0 +1,47 @@ + $rules['memo'], + 'allowed_ips' => [...$rules['allowed_ips'], 'max:50'], + 'allowed_ips.*' => 'string', + ]; + } + + /** + * Check that each of the values entered is actually valid. + */ + public function withValidator(Validator $validator): void + { + $validator->after(function (Validator $validator) { + if (!is_array($ips = $this->input('allowed_ips'))) { + return; + } + + foreach ($ips as $index => $ip) { + $valid = false; + try { + $valid = Range::parse($ip)->valid(); + } catch (\Exception $exception) { + if ($exception->getMessage() !== 'Invalid IP address format') { + throw $exception; + } + } finally { + $validator->errors()->addIf(!$valid, "allowed_ips.{$index}", '"' . $ip . '" is not a valid IP address or CIDR range.'); + } + } + }); + } +} diff --git a/app/Http/Requests/Api/Client/Account/StoreSSHKeyRequest.php b/app/Http/Requests/Api/Client/Account/StoreSSHKeyRequest.php new file mode 100644 index 000000000..3c8cf89ed --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/StoreSSHKeyRequest.php @@ -0,0 +1,78 @@ + UserSSHKey::getRulesForField('name'), + 'public_key' => UserSSHKey::getRulesForField('public_key'), + ]; + } + + /** + * Check to see if this SSH key has already been added to the user's account + * and if so return an error. + */ + public function withValidator(Validator $validator): void + { + $validator->after(function () { + try { + $this->key = PublicKeyLoader::loadPublicKey($this->input('public_key')); + } catch (NoKeyLoadedException $exception) { + $this->validator->errors()->add('public_key', 'The public key provided is not valid.'); + + return; + } + + if ($this->key instanceof DSA) { + $this->validator->errors()->add('public_key', 'DSA keys are not supported.'); + } + + if ($this->key instanceof RSA && $this->key->getLength() < 2048) { + $this->validator->errors()->add('public_key', 'RSA keys must be at least 2048 bytes in length.'); + } + + $fingerprint = $this->key->getFingerprint('sha256'); + if ($this->user()->sshKeys()->where('fingerprint', $fingerprint)->exists()) { + $this->validator->errors()->add('public_key', 'The public key provided already exists on your account.'); + } + }); + } + + /** + * Returns the public key but formatted in a consistent manner. + */ + public function getPublicKey(): string + { + return $this->key->toString('PKCS8'); + } + + /** + * Returns the SHA256 fingerprint of the key provided. + */ + public function getKeyFingerprint(): string + { + if (!$this->key) { + throw new \Exception('The public key was not properly loaded for this request.'); + } + + return $this->key->getFingerprint('sha256'); + } +} diff --git a/app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php b/app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php new file mode 100644 index 000000000..083e94058 --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php @@ -0,0 +1,39 @@ +make(Hasher::class); + + // Verify password matches when changing password or email. + if (!$hasher->check($this->input('password'), $this->user()->password)) { + throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password')); + } + + return true; + } + + public function rules(): array + { + $rules = User::getRulesForUpdate($this->user()); + + return ['email' => $rules['email']]; + } +} diff --git a/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php b/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php new file mode 100644 index 000000000..de8215b07 --- /dev/null +++ b/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php @@ -0,0 +1,37 @@ +make(Hasher::class); + + // Verify password matches when changing password or email. + if (!$hasher->check($this->input('current_password'), $this->user()->password)) { + throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password')); + } + + return true; + } + + public function rules(): array + { + return [ + 'password' => ['required', 'string', 'confirmed', 'min:8'], + ]; + } +} diff --git a/app/Http/Requests/Api/Client/ClientApiRequest.php b/app/Http/Requests/Api/Client/ClientApiRequest.php index 92402e513..5ae1680a4 100644 --- a/app/Http/Requests/Api/Client/ClientApiRequest.php +++ b/app/Http/Requests/Api/Client/ClientApiRequest.php @@ -2,18 +2,32 @@ namespace Pterodactyl\Http\Requests\Api\Client; +use Pterodactyl\Models\Server; +use Pterodactyl\Contracts\Http\ClientPermissionsRequest; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; -abstract class ClientApiRequest extends ApplicationApiRequest +/** + * @method \Pterodactyl\Models\User user($guard = null) + */ +class ClientApiRequest extends ApplicationApiRequest { /** - * Determine if the current user is authorized to perform - * the requested action against the API. - * - * @return bool + * Determine if the current user is authorized to perform the requested action against the API. */ public function authorize(): bool { + if ($this instanceof ClientPermissionsRequest || method_exists($this, 'permission')) { + $server = $this->route()->parameter('server'); + + if ($server instanceof Server) { + return $this->user()->can($this->permission(), $server); + } + + // If there is no server available on the reqest, trigger a failure since + // we expect there to be one at this point. + return false; + } + return true; } } diff --git a/app/Http/Requests/Api/Client/GetServersRequest.php b/app/Http/Requests/Api/Client/GetServersRequest.php index 9b4601f25..28b4c2ded 100644 --- a/app/Http/Requests/Api/Client/GetServersRequest.php +++ b/app/Http/Requests/Api/Client/GetServersRequest.php @@ -4,9 +4,6 @@ namespace Pterodactyl\Http\Requests\Api\Client; class GetServersRequest extends ClientApiRequest { - /** - * @return bool - */ public function authorize(): bool { return true; diff --git a/app/Http/Requests/Api/Client/Servers/Backups/RestoreBackupRequest.php b/app/Http/Requests/Api/Client/Servers/Backups/RestoreBackupRequest.php new file mode 100644 index 000000000..d2d427f99 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Backups/RestoreBackupRequest.php @@ -0,0 +1,19 @@ + 'required|boolean']; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Backups/StoreBackupRequest.php b/app/Http/Requests/Api/Client/Servers/Backups/StoreBackupRequest.php new file mode 100644 index 000000000..2871c039c --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Backups/StoreBackupRequest.php @@ -0,0 +1,23 @@ + 'nullable|string|max:191', + 'is_locked' => 'nullable|boolean', + 'ignored' => 'nullable|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php b/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php new file mode 100644 index 000000000..eb2cbc57e --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php @@ -0,0 +1,15 @@ +route()->parameter('server'); + + Assert::isInstanceOf($server, Server::class); + + return [ + 'database' => [ + 'required', + 'alpha_dash', + 'min:3', + 'max:48', + // Yes, I am aware that you could have the same database name across two unique hosts. However, + // I don't really care about that for this validation. We just want to make sure it is unique to + // the server itself. No need for complexity. + Rule::unique('databases')->where(function (Builder $query) use ($server) { + $query->where('server_id', $server->id) + ->where('database', DatabaseManagementService::generateUniqueDatabaseName($this->input('database'), $server->id)); + }), + ], + 'remote' => Database::getRulesForField('remote'), + ]; + } + + public function messages(): array + { + return [ + 'database.unique' => 'The database name you have selected is already in use by this server.', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php new file mode 100644 index 000000000..bf94cb6ba --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php @@ -0,0 +1,25 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.*.file' => 'required|string', + 'files.*.mode' => 'required|numeric', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/CompressFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CompressFilesRequest.php new file mode 100644 index 000000000..3d6e96aac --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/CompressFilesRequest.php @@ -0,0 +1,26 @@ + 'sometimes|nullable|string', + 'files' => 'required|array', + 'files.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php new file mode 100644 index 000000000..686c23a23 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php @@ -0,0 +1,22 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php new file mode 100644 index 000000000..e4655575d --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php @@ -0,0 +1,25 @@ + 'sometimes|nullable|string', + 'name' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/DecompressFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DecompressFilesRequest.php new file mode 100644 index 000000000..6e24682e4 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/DecompressFilesRequest.php @@ -0,0 +1,27 @@ + 'sometimes|nullable|string', + 'file' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php new file mode 100644 index 000000000..10db32811 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php @@ -0,0 +1,24 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php new file mode 100644 index 000000000..af1022bdc --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php @@ -0,0 +1,18 @@ +user()->can('file.read', $this->route()->parameter('server')); + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php new file mode 100644 index 000000000..ea6ed7b69 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php @@ -0,0 +1,27 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php new file mode 100644 index 000000000..25443148f --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php @@ -0,0 +1,25 @@ + 'sometimes|nullable|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/PullFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/PullFileRequest.php new file mode 100644 index 000000000..5f7648247 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/PullFileRequest.php @@ -0,0 +1,26 @@ + 'required|string|url', + 'directory' => 'nullable|string', + 'filename' => 'nullable|string', + 'use_header' => 'boolean', + 'foreground' => 'boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php new file mode 100644 index 000000000..61c4a0c5f --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php @@ -0,0 +1,30 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.*' => 'array', + 'files.*.to' => 'required|string', + 'files.*.from' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Files/UploadFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/UploadFileRequest.php new file mode 100644 index 000000000..a591fdf68 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/UploadFileRequest.php @@ -0,0 +1,14 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/GetServerRequest.php b/app/Http/Requests/Api/Client/Servers/GetServerRequest.php index b69203fc1..3798d77ab 100644 --- a/app/Http/Requests/Api/Client/Servers/GetServerRequest.php +++ b/app/Http/Requests/Api/Client/Servers/GetServerRequest.php @@ -10,8 +10,6 @@ class GetServerRequest extends ClientApiRequest * Determine if a client has permission to view this server on the API. This * should never be false since this would be checking the same permission as * resourceExists(). - * - * @return bool */ public function authorize(): bool { diff --git a/app/Http/Requests/Api/Client/Servers/Network/DeleteAllocationRequest.php b/app/Http/Requests/Api/Client/Servers/Network/DeleteAllocationRequest.php new file mode 100644 index 000000000..b7f6ca7b2 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Network/DeleteAllocationRequest.php @@ -0,0 +1,14 @@ + array_merge($rules['notes'], ['present']), + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/DeleteScheduleRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/DeleteScheduleRequest.php new file mode 100644 index 000000000..19d972265 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Schedules/DeleteScheduleRequest.php @@ -0,0 +1,13 @@ + $rules['name'], + 'is_active' => array_merge(['filled'], $rules['is_active']), + 'minute' => $rules['cron_minute'], + 'hour' => $rules['cron_hour'], + 'day_of_month' => $rules['cron_day_of_month'], + 'day_of_week' => $rules['cron_day_of_week'], + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php new file mode 100644 index 000000000..5ceb7c8e0 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php @@ -0,0 +1,29 @@ + 'required|in:command,power,backup', + 'payload' => 'required_unless:action,backup|string|nullable', + 'time_offset' => 'required|numeric|min:0|max:900', + 'sequence_id' => 'sometimes|required|numeric|min:1', + 'continue_on_failure' => 'sometimes|required|boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php new file mode 100644 index 000000000..216274acf --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php @@ -0,0 +1,19 @@ +route()->parameter('server'); + $schedule = $this->route()->parameter('schedule'); + + // If the schedule does not belong to this server throw a 404 error. Also throw an + // error if the task being requested does not belong to the associated schedule. + if ($server instanceof Server && $schedule instanceof Schedule) { + $task = $this->route()->parameter('task'); + + if ($schedule->server_id !== $server->id || ($task instanceof Task && $task->schedule_id !== $schedule->id)) { + throw new NotFoundHttpException('The requested resource does not exist on the system.'); + } + } + + return true; + } + + public function permission(): string + { + return Permission::ACTION_SCHEDULE_READ; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php b/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php index 22feb2afe..171cd19d9 100644 --- a/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php +++ b/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php @@ -2,24 +2,21 @@ namespace Pterodactyl\Http\Requests\Api\Client\Servers; -use Pterodactyl\Models\Server; +use Pterodactyl\Models\Permission; +use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest; -class SendCommandRequest extends GetServerRequest +class SendCommandRequest extends ClientApiRequest { /** * Determine if the API user has permission to perform this action. - * - * @return bool */ - public function authorize(): bool + public function permission(): string { - return $this->user()->can('send-command', $this->getModel(Server::class)); + return Permission::ACTION_CONTROL_CONSOLE; } /** * Rules to validate this request against. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php index 7a32c117b..e071388cf 100644 --- a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php +++ b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php @@ -2,25 +2,31 @@ namespace Pterodactyl\Http\Requests\Api\Client\Servers; -use Pterodactyl\Models\Server; +use Pterodactyl\Models\Permission; use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest; class SendPowerRequest extends ClientApiRequest { /** * Determine if the user has permission to send a power command to a server. - * - * @return bool */ - public function authorize(): bool + public function permission(): string { - return $this->user()->can('power-' . $this->input('signal', '_undefined'), $this->getModel(Server::class)); + switch ($this->input('signal')) { + case 'start': + return Permission::ACTION_CONTROL_START; + case 'stop': + case 'kill': + return Permission::ACTION_CONTROL_STOP; + case 'restart': + return Permission::ACTION_CONTROL_RESTART; + } + + return '__invalid'; } /** * Rules to validate this request against. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php b/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php new file mode 100644 index 000000000..63f01dbae --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php @@ -0,0 +1,14 @@ + Server::getRules()['name'], + 'description' => 'string|nullable', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Settings/SetDockerImageRequest.php b/app/Http/Requests/Api/Client/Servers/Settings/SetDockerImageRequest.php new file mode 100644 index 000000000..f618de370 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Settings/SetDockerImageRequest.php @@ -0,0 +1,30 @@ +route()->parameter('server'); + + Assert::isInstanceOf($server, Server::class); + + return [ + 'docker_image' => ['required', 'string', Rule::in(array_values($server->egg->docker_images))], + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Startup/GetStartupRequest.php b/app/Http/Requests/Api/Client/Servers/Startup/GetStartupRequest.php new file mode 100644 index 000000000..fee92bcbc --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Startup/GetStartupRequest.php @@ -0,0 +1,14 @@ + 'required|string', + 'value' => 'present', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php new file mode 100644 index 000000000..eabd84e61 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php @@ -0,0 +1,13 @@ + 'required|email|between:1,191', + 'permissions' => 'required|array', + 'permissions.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php new file mode 100644 index 000000000..1381e5cb9 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php @@ -0,0 +1,73 @@ +route()->parameter('user'); + // Don't allow a user to edit themselves on the server. + if ($user instanceof User) { + if ($user->uuid === $this->user()->uuid) { + return false; + } + } + + // If this is a POST request, validate that the user can even assign the permissions they + // have selected to assign. + if ($this->method() === Request::METHOD_POST && $this->has('permissions')) { + $this->validatePermissionsCanBeAssigned( + $this->input('permissions') ?? [] + ); + } + + return true; + } + + /** + * Validates that the permissions we are trying to assign can actually be assigned + * by the user making the request. + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + protected function validatePermissionsCanBeAssigned(array $permissions) + { + $user = $this->user(); + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->route()->parameter('server'); + + // If we are a root admin or the server owner, no need to perform these checks. + if ($user->root_admin || $user->id === $server->owner_id) { + return; + } + + // Otherwise, get the current subuser's permission set, and ensure that the + // permissions they are trying to assign are not _more_ than the ones they + // already have. + /** @var \Pterodactyl\Services\Servers\GetUserPermissionsService $service */ + $service = $this->container->make(GetUserPermissionsService::class); + + if (count(array_diff($permissions, $service->handle($server, $user))) > 0) { + throw new HttpForbiddenException('Cannot assign permissions to a subuser that your account does not actively possess.'); + } + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php new file mode 100644 index 000000000..bd8929a98 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php @@ -0,0 +1,21 @@ + 'required|array', + 'permissions.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/ActivityEventRequest.php b/app/Http/Requests/Api/Remote/ActivityEventRequest.php new file mode 100644 index 000000000..43f527dfa --- /dev/null +++ b/app/Http/Requests/Api/Remote/ActivityEventRequest.php @@ -0,0 +1,50 @@ + ['required', 'array'], + 'data.*' => ['array'], + 'data.*.user' => ['sometimes', 'nullable', 'uuid'], + 'data.*.server' => ['required', 'uuid'], + 'data.*.event' => ['required', 'string'], + 'data.*.metadata' => ['present', 'nullable', 'array'], + 'data.*.ip' => ['sometimes', 'nullable', 'ip'], + 'data.*.timestamp' => ['required', 'string'], + ]; + } + + /** + * Returns all the unique server UUIDs that were received in this request. + */ + public function servers(): array + { + return Collection::make($this->input('data'))->pluck('server')->unique()->toArray(); + } + + /** + * Returns all the unique user UUIDs that were submitted in this request. + */ + public function users(): array + { + return Collection::make($this->input('data')) + ->filter(function ($value) { + return !empty($value['user']); + }) + ->pluck('user') + ->unique() + ->toArray(); + } +} diff --git a/app/Http/Requests/Api/Remote/AuthenticateWebsocketDetailsRequest.php b/app/Http/Requests/Api/Remote/AuthenticateWebsocketDetailsRequest.php new file mode 100644 index 000000000..1bae01dd7 --- /dev/null +++ b/app/Http/Requests/Api/Remote/AuthenticateWebsocketDetailsRequest.php @@ -0,0 +1,20 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/InstallationDataRequest.php b/app/Http/Requests/Api/Remote/InstallationDataRequest.php new file mode 100644 index 000000000..13b3e77d7 --- /dev/null +++ b/app/Http/Requests/Api/Remote/InstallationDataRequest.php @@ -0,0 +1,21 @@ + 'present|boolean', + 'reinstall' => 'sometimes|boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/ReportBackupCompleteRequest.php b/app/Http/Requests/Api/Remote/ReportBackupCompleteRequest.php new file mode 100644 index 000000000..d0dd3090b --- /dev/null +++ b/app/Http/Requests/Api/Remote/ReportBackupCompleteRequest.php @@ -0,0 +1,21 @@ + 'required|boolean', + 'checksum' => 'nullable|string|required_if:successful,true', + 'checksum_type' => 'nullable|string|required_if:successful,true', + 'size' => 'nullable|numeric|required_if:successful,true', + 'parts' => 'nullable|array', + 'parts.*.etag' => 'required|string', + 'parts.*.part_number' => 'required|numeric', + ]; + } +} diff --git a/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php b/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php index 041ff197f..964c27974 100644 --- a/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php +++ b/app/Http/Requests/Api/Remote/SftpAuthenticationFormRequest.php @@ -8,34 +8,29 @@ class SftpAuthenticationFormRequest extends FormRequest { /** * Authenticate the request. - * - * @return bool */ - public function authorize() + public function authorize(): bool { return true; } /** * Rules to apply to the request. - * - * @return array */ - public function rules() + public function rules(): array { return [ - 'username' => 'required|string', - 'password' => 'required|string', + 'type' => ['nullable', 'in:password,public_key'], + 'username' => ['required', 'string'], + 'password' => ['required', 'string'], ]; } /** * Return only the fields that we are interested in from the request. * This will include empty fields as a null value. - * - * @return array */ - public function normalize() + public function normalize(): array { return $this->only( array_keys($this->rules()) diff --git a/app/Http/Requests/Auth/LoginCheckpointRequest.php b/app/Http/Requests/Auth/LoginCheckpointRequest.php new file mode 100644 index 000000000..3c9392a63 --- /dev/null +++ b/app/Http/Requests/Auth/LoginCheckpointRequest.php @@ -0,0 +1,41 @@ + 'required|string', + 'authentication_code' => [ + 'nullable', + 'numeric', + Rule::requiredIf(function () { + return empty($this->input('recovery_token')); + }), + ], + 'recovery_token' => [ + 'nullable', + 'string', + Rule::requiredIf(function () { + return empty($this->input('authentication_code')); + }), + ], + ]; + } +} diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 000000000..d08846929 --- /dev/null +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,21 @@ + 'required|string|min:1', + 'password' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Auth/ResetPasswordRequest.php b/app/Http/Requests/Auth/ResetPasswordRequest.php new file mode 100644 index 000000000..e5bb3299c --- /dev/null +++ b/app/Http/Requests/Auth/ResetPasswordRequest.php @@ -0,0 +1,22 @@ + 'required|string', + 'email' => 'required|email', + 'password' => 'required|string|confirmed|min:8', + ]; + } +} diff --git a/app/Http/Requests/Base/AccountDataFormRequest.php b/app/Http/Requests/Base/AccountDataFormRequest.php deleted file mode 100644 index 076cba9bd..000000000 --- a/app/Http/Requests/Base/AccountDataFormRequest.php +++ /dev/null @@ -1,71 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Base; - -use Pterodactyl\Models\User; -use Pterodactyl\Http\Requests\FrontendUserFormRequest; -use Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException; - -class AccountDataFormRequest extends FrontendUserFormRequest -{ - /** - * @return bool - * @throws \Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException - */ - public function authorize() - { - if (! parent::authorize()) { - return false; - } - - // Verify password matches when changing password or email. - if (in_array($this->input('do_action'), ['password', 'email'])) { - if (! password_verify($this->input('current_password'), $this->user()->password)) { - throw new InvalidPasswordProvidedException(trans('base.account.invalid_password')); - } - } - - return true; - } - - /** - * @return array - */ - public function rules() - { - $modelRules = User::getUpdateRulesForId($this->user()->id); - - switch ($this->input('do_action')) { - case 'email': - $rules = [ - 'new_email' => array_get($modelRules, 'email'), - ]; - break; - case 'password': - $rules = [ - 'new_password' => 'required|confirmed|string|min:8', - 'new_password_confirmation' => 'required', - ]; - break; - case 'identity': - $rules = [ - 'name_first' => array_get($modelRules, 'name_first'), - 'name_last' => array_get($modelRules, 'name_last'), - 'username' => array_get($modelRules, 'username'), - 'language' => array_get($modelRules, 'language'), - ]; - break; - default: - abort(422); - } - - return $rules; - } -} diff --git a/app/Http/Requests/Base/ApiKeyFormRequest.php b/app/Http/Requests/Base/ApiKeyFormRequest.php deleted file mode 100644 index 5959657f0..000000000 --- a/app/Http/Requests/Base/ApiKeyFormRequest.php +++ /dev/null @@ -1,74 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Base; - -use IPTools\Network; -use Pterodactyl\Http\Requests\FrontendUserFormRequest; - -class ApiKeyFormRequest extends FrontendUserFormRequest -{ - /** - * Rules applied to data passed in this request. - * - * @return array - */ - public function rules() - { - $this->parseAllowedIntoArray(); - - return [ - 'memo' => 'required|nullable|string|max:500', - 'permissions' => 'sometimes|present|array', - 'admin_permissions' => 'sometimes|present|array', - 'allowed_ips' => 'present', - 'allowed_ips.*' => 'sometimes|string', - ]; - } - - /** - * Parse the string of allowed IPs into an array. - */ - protected function parseAllowedIntoArray() - { - $loop = []; - if (! empty($this->input('allowed_ips'))) { - foreach (explode(PHP_EOL, $this->input('allowed_ips')) as $ip) { - $loop[] = trim($ip); - } - } - - $this->merge(['allowed_ips' => $loop]); - } - - /** - * Run additional validation rules on the request to ensure all of the data is good. - * - * @param \Illuminate\Validation\Validator $validator - */ - public function withValidator($validator) - { - $validator->after(function ($validator) { - /* @var \Illuminate\Validation\Validator $validator */ - if (empty($this->input('permissions')) && empty($this->input('admin_permissions'))) { - $validator->errors()->add('permissions', 'At least one permission must be selected.'); - } - - foreach ($this->input('allowed_ips') as $ip) { - $ip = trim($ip); - - try { - Network::parse($ip); - } catch (\Exception $ex) { - $validator->errors()->add('allowed_ips', 'Could not parse IP ' . $ip . ' because it is in an invalid format.'); - } - } - }); - } -} diff --git a/app/Http/Requests/Base/CreateClientApiKeyRequest.php b/app/Http/Requests/Base/CreateClientApiKeyRequest.php deleted file mode 100644 index b8e7bbfe2..000000000 --- a/app/Http/Requests/Base/CreateClientApiKeyRequest.php +++ /dev/null @@ -1,21 +0,0 @@ - 'required|string|max:255', - 'allowed_ips' => 'nullable|string', - ]; - } -} diff --git a/app/Http/Requests/Base/StoreAccountKeyRequest.php b/app/Http/Requests/Base/StoreAccountKeyRequest.php deleted file mode 100644 index 2cfc22786..000000000 --- a/app/Http/Requests/Base/StoreAccountKeyRequest.php +++ /dev/null @@ -1,23 +0,0 @@ - 'required|nullable|string|max:500', - 'allowed_ips' => 'present', - 'allowed_ips.*' => 'sometimes|string', - ]; - } -} diff --git a/app/Http/Requests/FrontendUserFormRequest.php b/app/Http/Requests/FrontendUserFormRequest.php index 6be818e7b..66d13d8c5 100644 --- a/app/Http/Requests/FrontendUserFormRequest.php +++ b/app/Http/Requests/FrontendUserFormRequest.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Http\Requests; @@ -13,25 +6,21 @@ use Illuminate\Foundation\Http\FormRequest; abstract class FrontendUserFormRequest extends FormRequest { - abstract public function rules(); + abstract public function rules(): array; /** * Determine if a user is authorized to access this endpoint. - * - * @return bool */ - public function authorize() + public function authorize(): bool { - return ! is_null($this->user()); + return !is_null($this->user()); } /** * Return only the fields that we are interested in from the request. * This will include empty fields as a null value. - * - * @return array */ - public function normalize() + public function normalize(): array { return $this->only( array_keys($this->rules()) diff --git a/app/Http/Requests/Server/Database/DeleteServerDatabaseRequest.php b/app/Http/Requests/Server/Database/DeleteServerDatabaseRequest.php deleted file mode 100644 index eed54e2e4..000000000 --- a/app/Http/Requests/Server/Database/DeleteServerDatabaseRequest.php +++ /dev/null @@ -1,40 +0,0 @@ - 'required|string|min:1', - 'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/', - ]; - } -} diff --git a/app/Http/Requests/Server/ScheduleCreationFormRequest.php b/app/Http/Requests/Server/ScheduleCreationFormRequest.php deleted file mode 100644 index 6291d3cb3..000000000 --- a/app/Http/Requests/Server/ScheduleCreationFormRequest.php +++ /dev/null @@ -1,79 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Server; - -class ScheduleCreationFormRequest extends ServerFormRequest -{ - /** - * Permission to validate this request against. - * - * @return string - */ - protected function permission(): string - { - if ($this->method() === 'PATCH') { - return 'edit-schedule'; - } - - return 'create-schedule'; - } - - /** - * Validation rules to apply to the request. - * - * @return array - */ - public function rules() - { - return [ - 'name' => 'nullable|string|max:255', - 'cron_day_of_week' => 'required|string', - 'cron_day_of_month' => 'required|string', - 'cron_hour' => 'required|string', - 'cron_minute' => 'required|string', - 'tasks' => 'sometimes|array|size:4', - 'tasks.time_value' => 'required_with:tasks|max:5', - 'tasks.time_interval' => 'required_with:tasks|max:5', - 'tasks.action' => 'required_with:tasks|max:5', - 'tasks.payload' => 'required_with:tasks|max:5', - 'tasks.time_value.*' => 'numeric|between:0,59', - 'tasks.time_interval.*' => 'string|in:s,m', - 'tasks.action.*' => 'string|in:power,command', - 'tasks.payload.*' => 'string', - ]; - } - - /** - * Normalize the request into a format that can be used by the application. - * - * @return array - */ - public function normalize() - { - return $this->only('name', 'cron_day_of_week', 'cron_day_of_month', 'cron_hour', 'cron_minute'); - } - - /** - * Return the tasks provided in the request that are associated with this schedule. - * - * @return array|null - */ - public function getTasks() - { - $restructured = []; - foreach (array_get($this->all(), 'tasks', []) as $key => $values) { - for ($i = 0; $i < count($values); $i++) { - $restructured[$i][$key] = $values[$i]; - } - } - - return empty($restructured) ? null : $restructured; - } -} diff --git a/app/Http/Requests/Server/ServerFormRequest.php b/app/Http/Requests/Server/ServerFormRequest.php deleted file mode 100644 index c0ca370b7..000000000 --- a/app/Http/Requests/Server/ServerFormRequest.php +++ /dev/null @@ -1,35 +0,0 @@ -user()->can($this->permission(), $this->getServer()); - } - - public function getServer(): Server - { - return $this->attributes->get('server'); - } -} diff --git a/app/Http/Requests/Server/Settings/ChangeServerNameRequest.php b/app/Http/Requests/Server/Settings/ChangeServerNameRequest.php deleted file mode 100644 index c969cb0e2..000000000 --- a/app/Http/Requests/Server/Settings/ChangeServerNameRequest.php +++ /dev/null @@ -1,31 +0,0 @@ - Server::getCreateRules()['name'], - ]; - } -} diff --git a/app/Http/Requests/Server/Subuser/SubuserStoreFormRequest.php b/app/Http/Requests/Server/Subuser/SubuserStoreFormRequest.php deleted file mode 100644 index 9b7c6ce4d..000000000 --- a/app/Http/Requests/Server/Subuser/SubuserStoreFormRequest.php +++ /dev/null @@ -1,31 +0,0 @@ - 'required|email', - 'permissions' => 'sometimes|array', - ]; - } -} diff --git a/app/Http/Requests/Server/Subuser/SubuserUpdateFormRequest.php b/app/Http/Requests/Server/Subuser/SubuserUpdateFormRequest.php deleted file mode 100644 index 7ff82abcb..000000000 --- a/app/Http/Requests/Server/Subuser/SubuserUpdateFormRequest.php +++ /dev/null @@ -1,30 +0,0 @@ - 'present|array', - ]; - } -} diff --git a/app/Http/Requests/Server/UpdateFileContentsFormRequest.php b/app/Http/Requests/Server/UpdateFileContentsFormRequest.php deleted file mode 100644 index 7ded39bc0..000000000 --- a/app/Http/Requests/Server/UpdateFileContentsFormRequest.php +++ /dev/null @@ -1,101 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Server; - -use GuzzleHttp\Exception\RequestException; -use Illuminate\Contracts\Config\Repository; -use Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; -use Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; - -class UpdateFileContentsFormRequest extends ServerFormRequest -{ - /** - * Return the permission string to validate this request against. - * - * @return string - */ - protected function permission(): string - { - return 'edit-files'; - } - - /** - * Authorize a request to edit a file. - * - * @return bool - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException - * @throws \Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function authorize() - { - if (! parent::authorize()) { - return false; - } - - $server = $this->attributes->get('server'); - $token = $this->attributes->get('server_token'); - - return $this->checkFileCanBeEdited($server, $token); - } - - /** - * @return array - */ - public function rules() - { - return []; - } - - /** - * Checks if a given file can be edited by a user on this server. - * - * @param \Pterodactyl\Models\Server $server - * @param string $token - * @return bool - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException - * @throws \Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException - */ - private function checkFileCanBeEdited($server, $token) - { - $config = app()->make(Repository::class); - $repository = app()->make(FileRepositoryInterface::class); - - try { - $stats = $repository->setServer($server)->setToken($token)->getFileStat($this->route()->parameter('file')); - } catch (RequestException $exception) { - switch ($exception->getCode()) { - case 404: - throw new NotFoundHttpException; - default: - throw new DaemonConnectionException($exception); - } - } - - if ((! $stats->file && ! $stats->symlink) || ! in_array($stats->mime, $config->get('pterodactyl.files.editable'))) { - throw new FileTypeNotEditableException(trans('server.files.exceptions.invalid_mime')); - } - - if ($stats->size > $config->get('pterodactyl.files.max_edit_size')) { - throw new FileSizeTooLargeException(trans('server.files.exceptions.max_size')); - } - - $this->attributes->set('file_stats', $stats); - - return true; - } -} diff --git a/app/Http/Requests/Server/UpdateStartupParametersFormRequest.php b/app/Http/Requests/Server/UpdateStartupParametersFormRequest.php deleted file mode 100644 index 41c15103f..000000000 --- a/app/Http/Requests/Server/UpdateStartupParametersFormRequest.php +++ /dev/null @@ -1,61 +0,0 @@ -user()->can('edit-startup', $this->attributes->get('server')); - } - - /** - * Validate that all of the required fields were passed and that the environment - * variable values meet the defined criteria for those fields. - * - * @return array - */ - public function rules() - { - $repository = $this->container->make(EggVariableRepositoryInterface::class); - - $variables = $repository->getEditableVariables($this->attributes->get('server')->egg_id); - $rules = $variables->mapWithKeys(function ($variable) { - $this->validationAttributes['environment.' . $variable->env_variable] = $variable->name; - - return ['environment.' . $variable->env_variable => $variable->rules]; - })->toArray(); - - return array_merge($rules, [ - 'environment' => 'required|array', - ]); - } - - /** - * Return attributes to provide better naming conventions for error messages. - * - * @return array - */ - public function attributes() - { - return $this->validationAttributes; - } -} diff --git a/app/Http/Resources/Wings/ServerConfigurationCollection.php b/app/Http/Resources/Wings/ServerConfigurationCollection.php new file mode 100644 index 000000000..84fc2f811 --- /dev/null +++ b/app/Http/Resources/Wings/ServerConfigurationCollection.php @@ -0,0 +1,32 @@ +make(EggConfigurationService::class); + $configuration = Container::getInstance()->make(ServerConfigurationStructureService::class); + + return $this->collection->map(function (Server $server) use ($configuration, $egg) { + return [ + 'uuid' => $server->uuid, + 'settings' => $configuration->handle($server), + 'process_configuration' => $egg->handle($server), + ]; + })->toArray(); + } +} diff --git a/app/Http/ViewComposers/AssetComposer.php b/app/Http/ViewComposers/AssetComposer.php new file mode 100644 index 000000000..2bbb39d5c --- /dev/null +++ b/app/Http/ViewComposers/AssetComposer.php @@ -0,0 +1,23 @@ +with('siteConfiguration', [ + 'name' => config('app.name') ?? 'Pterodactyl', + 'locale' => config('app.locale') ?? 'en', + 'recaptcha' => [ + 'enabled' => config('recaptcha.enabled', false), + 'siteKey' => config('recaptcha.website_key') ?? '', + ], + ]); + } +} diff --git a/app/Http/ViewComposers/Server/ServerDataComposer.php b/app/Http/ViewComposers/Server/ServerDataComposer.php deleted file mode 100644 index 9e1858645..000000000 --- a/app/Http/ViewComposers/Server/ServerDataComposer.php +++ /dev/null @@ -1,38 +0,0 @@ -request = $request; - } - - /** - * Attach server data to a view automatically. - * - * @param \Illuminate\View\View $view - */ - public function compose(View $view) - { - $server = $this->request->get('server'); - - $view->with('server', $server); - $view->with('node', object_get($server, 'node')); - $view->with('daemon_token', $this->request->get('server_token')); - } -} diff --git a/app/Http/ViewComposers/ServerListComposer.php b/app/Http/ViewComposers/ServerListComposer.php deleted file mode 100644 index 4c3ac71fb..000000000 --- a/app/Http/ViewComposers/ServerListComposer.php +++ /dev/null @@ -1,51 +0,0 @@ -request = $request; - $this->repository = $repository; - } - - /** - * Attach a list of servers the user can access to the view. - * - * @param \Illuminate\View\View $view - */ - public function compose(View $view) - { - if (! $this->request->user()) { - return; - } - - $servers = $this->repository - ->setColumns(['id', 'owner_id', 'uuidShort', 'name', 'description']) - ->filterUserAccessServers($this->request->user(), User::FILTER_LEVEL_SUBUSER, false); - - $view->with('sidebarServerList', $servers); - } -} diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index 7f206ec8f..c8a896912 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -2,125 +2,92 @@ namespace Pterodactyl\Jobs\Schedule; -use Exception; -use Cake\Chronos\Chronos; use Pterodactyl\Jobs\Job; -use InvalidArgumentException; +use Carbon\CarbonImmutable; +use Pterodactyl\Models\Task; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\DispatchesJobs; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; -use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; -use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; +use Pterodactyl\Services\Backups\InitiateBackupService; +use Pterodactyl\Repositories\Wings\DaemonPowerRepository; +use Pterodactyl\Repositories\Wings\DaemonCommandRepository; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class RunTaskJob extends Job implements ShouldQueue { - use DispatchesJobs, InteractsWithQueue, SerializesModels; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface - */ - protected $commandRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface - */ - protected $powerRepository; - - /** - * @var int - */ - public $schedule; - - /** - * @var int - */ - public $task; - - /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface - */ - protected $taskRepository; + use DispatchesJobs; + use InteractsWithQueue; + use SerializesModels; /** * RunTaskJob constructor. - * - * @param int $task - * @param int $schedule */ - public function __construct(int $task, int $schedule) + public function __construct(public Task $task, public bool $manualRun = false) { - $this->queue = config('pterodactyl.queues.standard'); - $this->task = $task; - $this->schedule = $schedule; + $this->queue = 'standard'; } /** * Run the job and send actions to the daemon running the server. * - * @param \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface $commandRepository - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService - * @param \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface $powerRepository - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $taskRepository - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ public function handle( - CommandRepositoryInterface $commandRepository, - DaemonKeyProviderService $keyProviderService, - PowerRepositoryInterface $powerRepository, - TaskRepositoryInterface $taskRepository + DaemonCommandRepository $commandRepository, + InitiateBackupService $backupService, + DaemonPowerRepository $powerRepository ) { - $this->commandRepository = $commandRepository; - $this->powerRepository = $powerRepository; - $this->taskRepository = $taskRepository; - - $task = $this->taskRepository->getTaskForJobProcess($this->task); - $server = $task->getRelation('server'); - $user = $server->getRelation('user'); - - // Do not process a task that is not set to active. - if (! $task->getRelation('schedule')->is_active) { + // Do not process a task that is not set to active, unless it's been manually triggered. + if (!$this->task->schedule->is_active && !$this->manualRun) { $this->markTaskNotQueued(); $this->markScheduleComplete(); return; } + $server = $this->task->server; + // If we made it to this point and the server status is not null it means the + // server was likely suspended or marked as reinstalling after the schedule + // was queued up. Just end the task right now — this should be a very rare + // condition. + if (!is_null($server->status)) { + $this->failed(); + + return; + } + // Perform the provided task against the daemon. - switch ($task->action) { - case 'power': - $this->powerRepository->setServer($server) - ->setToken($keyProviderService->handle($server, $user)) - ->sendSignal($task->payload); - break; - case 'command': - $this->commandRepository->setServer($server) - ->setToken($keyProviderService->handle($server, $user)) - ->send($task->payload); - break; - default: - throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.'); + try { + switch ($this->task->action) { + case Task::ACTION_POWER: + $powerRepository->setServer($server)->send($this->task->payload); + break; + case Task::ACTION_COMMAND: + $commandRepository->setServer($server)->send($this->task->payload); + break; + case Task::ACTION_BACKUP: + $backupService->setIgnoredFiles(explode(PHP_EOL, $this->task->payload))->handle($server, null, true); + break; + default: + throw new \InvalidArgumentException('Invalid task action provided: ' . $this->task->action); + } + } catch (\Exception $exception) { + // If this isn't a DaemonConnectionException on a task that allows for failures + // throw the exception back up the chain so that the task is stopped. + if (!($this->task->continue_on_failure && $exception instanceof DaemonConnectionException)) { + throw $exception; + } } $this->markTaskNotQueued(); - $this->queueNextTask($task->sequence_id); + $this->queueNextTask(); } /** * Handle a failure while sending the action to the daemon or otherwise processing the job. - * - * @param null|\Exception $exception - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function failed(Exception $exception = null) + public function failed(\Exception $exception = null) { $this->markTaskNotQueued(); $this->markScheduleComplete(); @@ -128,49 +95,42 @@ class RunTaskJob extends Job implements ShouldQueue /** * Get the next task in the schedule and queue it for running after the defined period of wait time. - * - * @param int $sequence - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - private function queueNextTask($sequence) + private function queueNextTask() { - $nextTask = $this->taskRepository->getNextTask($this->schedule, $sequence); + /** @var \Pterodactyl\Models\Task|null $nextTask */ + $nextTask = Task::query()->where('schedule_id', $this->task->schedule_id) + ->orderBy('sequence_id', 'asc') + ->where('sequence_id', '>', $this->task->sequence_id) + ->first(); + if (is_null($nextTask)) { $this->markScheduleComplete(); return; } - $this->taskRepository->update($nextTask->id, ['is_queued' => true]); - $this->dispatch((new self($nextTask->id, $this->schedule))->delay($nextTask->time_offset)); + $nextTask->update(['is_queued' => true]); + + $this->dispatch((new self($nextTask, $this->manualRun))->delay($nextTask->time_offset)); } /** * Marks the parent schedule as being complete. - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ private function markScheduleComplete() { - $repository = app()->make(ScheduleRepositoryInterface::class); - $repository->withoutFreshModel()->update($this->schedule, [ + $this->task->schedule()->update([ 'is_processing' => false, - 'last_run_at' => Chronos::now()->toDateTimeString(), + 'last_run_at' => CarbonImmutable::now()->toDateTimeString(), ]); } /** * Mark a specific task as no longer being queued. - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ private function markTaskNotQueued() { - $repository = app()->make(TaskRepositoryInterface::class); - $repository->update($this->task, ['is_queued' => false]); + $this->task->update(['is_queued' => false]); } } diff --git a/app/Listeners/Auth/AuthenticationListener.php b/app/Listeners/Auth/AuthenticationListener.php new file mode 100644 index 000000000..ec25b19c5 --- /dev/null +++ b/app/Listeners/Auth/AuthenticationListener.php @@ -0,0 +1,38 @@ +user) { + $activity = $activity->subject($event->user); + } + + if ($event instanceof Failed) { + foreach ($event->credentials as $key => $value) { + $activity = $activity->property($key, $value); + } + } + + $activity->event($event instanceof Failed ? 'auth:fail' : 'auth:success')->log(); + } + + public function subscribe(Dispatcher $events): void + { + $events->listen(Failed::class, self::class); + $events->listen(DirectLogin::class, self::class); + } +} diff --git a/app/Listeners/Auth/PasswordResetListener.php b/app/Listeners/Auth/PasswordResetListener.php new file mode 100644 index 000000000..27ed19c69 --- /dev/null +++ b/app/Listeners/Auth/PasswordResetListener.php @@ -0,0 +1,25 @@ +request = $request; + } + + public function handle(PasswordReset $event): void + { + Activity::event('event:password-reset') + ->withRequestMetadata() + ->subject($event->user) + ->log(); + } +} diff --git a/app/Listeners/Auth/TwoFactorListener.php b/app/Listeners/Auth/TwoFactorListener.php new file mode 100644 index 000000000..91d920887 --- /dev/null +++ b/app/Listeners/Auth/TwoFactorListener.php @@ -0,0 +1,17 @@ +recovery ? 'auth:recovery-token' : 'auth:token') + ->withRequestMetadata() + ->subject($event->user) + ->log(); + } +} diff --git a/app/Models/APILog.php b/app/Models/APILog.php index 359daa4ed..673f94e73 100644 --- a/app/Models/APILog.php +++ b/app/Models/APILog.php @@ -8,29 +8,21 @@ class APILog extends Model { /** * The table associated with the model. - * - * @var string */ protected $table = 'api_logs'; /** * The attributes excluded from the model's JSON form. - * - * @var array */ protected $hidden = []; /** * Fields that are not mass assignable. - * - * @var array */ protected $guarded = ['id', 'created_at', 'updated_at']; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'authorized' => 'boolean', diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php new file mode 100644 index 000000000..e8f8a4e30 --- /dev/null +++ b/app/Models/ActivityLog.php @@ -0,0 +1,144 @@ + 'collection', + 'timestamp' => 'datetime', + ]; + + protected $with = ['subjects']; + + public static array $validationRules = [ + 'event' => ['required', 'string'], + 'batch' => ['nullable', 'uuid'], + 'ip' => ['required', 'string'], + 'description' => ['nullable', 'string'], + 'properties' => ['array'], + ]; + + public function actor(): MorphTo + { + $morph = $this->morphTo(); + if (method_exists($morph, 'withTrashed')) { + return $morph->withTrashed(); + } + + return $morph; + } + + public function subjects(): HasMany + { + return $this->hasMany(ActivityLogSubject::class); + } + + public function apiKey(): HasOne + { + return $this->hasOne(ApiKey::class, 'id', 'api_key_id'); + } + + public function scopeForEvent(Builder $builder, string $action): Builder + { + return $builder->where('event', $action); + } + + /** + * Scopes a query to only return results where the actor is a given model. + */ + public function scopeForActor(Builder $builder, IlluminateModel $actor): Builder + { + return $builder->whereMorphedTo('actor', $actor); + } + + /** + * Returns models to be pruned. + * + * @see https://laravel.com/docs/9.x/eloquent#pruning-models + */ + public function prunable() + { + if (is_null(config('activity.prune_days'))) { + throw new \LogicException('Cannot prune activity logs: no "prune_days" configuration value is set.'); + } + + return static::where('timestamp', '<=', Carbon::now()->subDays(config('activity.prune_days'))); + } + + /** + * Boots the model event listeners. This will trigger an activity log event every + * time a new model is inserted which can then be captured and worked with as needed. + */ + protected static function boot() + { + parent::boot(); + + static::created(function (self $model) { + Event::dispatch(new ActivityLogged($model)); + }); + } +} diff --git a/app/Models/ActivityLogSubject.php b/app/Models/ActivityLogSubject.php new file mode 100644 index 000000000..6629b3e7d --- /dev/null +++ b/app/Models/ActivityLogSubject.php @@ -0,0 +1,46 @@ +belongsTo(ActivityLog::class); + } + + public function subject() + { + $morph = $this->morphTo(); + if (method_exists($morph, 'withTrashed')) { + return $morph->withTrashed(); + } + + return $morph; + } +} diff --git a/app/Models/AdminRole.php b/app/Models/AdminRole.php new file mode 100644 index 000000000..f03368392 --- /dev/null +++ b/app/Models/AdminRole.php @@ -0,0 +1,59 @@ + 'int', + 'permissions' => 'array', + ]; + + public static array $validationRules = [ + 'name' => 'required|string|max:64', + 'description' => 'nullable|string|max:255', + 'sort_id' => 'sometimes|numeric', + ]; + + public $timestamps = false; + + /** + * Gets the permissions associated with an admin role. + */ + public function permissions(): HasMany + { + return $this->hasMany(Permission::class); + } +} diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index 5921c0a2b..d16e3150c 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -2,40 +2,62 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\BelongsTo; -class Allocation extends Model implements CleansAttributes, ValidableContract +/** + * Pterodactyl\Models\Allocation. + * + * @property int $id + * @property int $node_id + * @property string $ip + * @property string|null $ip_alias + * @property int $port + * @property int|null $server_id + * @property string|null $notes + * @property \Carbon\Carbon|null $created_at + * @property \Carbon\Carbon|null $updated_at + * @property string $alias + * @property bool $has_alias + * @property \Pterodactyl\Models\Server|null $server + * @property \Pterodactyl\Models\Node $node + * @property string $hashid + * + * @method static \Database\Factories\AllocationFactory factory(...$parameters) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Allocation newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Allocation query() + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereIp($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereIpAlias($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereNodeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation wherePort($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereServerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Allocation whereUpdatedAt($value) + * + * @mixin \Eloquent + */ +class Allocation extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'allocation'; + public const RESOURCE_NAME = 'allocation'; /** * The table associated with the model. - * - * @var string */ protected $table = 'allocations'; /** * Fields that are not mass assignable. - * - * @var array */ protected $guarded = ['id', 'created_at', 'updated_at']; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'node_id' => 'integer', @@ -43,74 +65,64 @@ class Allocation extends Model implements CleansAttributes, ValidableContract 'server_id' => 'integer', ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'node_id' => 'required', - 'ip' => 'required', - 'port' => 'required', + public static array $validationRules = [ + 'node_id' => 'required|exists:nodes,id', + 'ip' => 'required|ip', + 'port' => 'required|numeric|between:1024,65535', + 'ip_alias' => 'nullable|string', + 'server_id' => 'nullable|exists:servers,id', + 'notes' => 'nullable|string|max:256', ]; /** - * @var array + * {@inheritDoc} */ - protected static $dataIntegrityRules = [ - 'node_id' => 'exists:nodes,id', - 'ip' => 'ip', - 'port' => 'numeric|between:1024,65553', - 'ip_alias' => 'nullable|string', - 'server_id' => 'nullable|exists:servers,id', - ]; + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } /** * Return a hashid encoded string to represent the ID of the allocation. - * - * @return string */ - public function getHashidAttribute() + public function getHashidAttribute(): string { return app()->make('hashids')->encode($this->id); } /** * Accessor to automatically provide the IP alias if defined. - * - * @param null|string $value - * @return string */ - public function getAliasAttribute($value) + public function getAliasAttribute(?string $value): string { return (is_null($this->ip_alias)) ? $this->ip : $this->ip_alias; } /** * Accessor to quickly determine if this allocation has an alias. - * - * @param null|string $value - * @return bool */ - public function getHasAliasAttribute($value) + public function getHasAliasAttribute(?string $value): bool { - return ! is_null($this->ip_alias); + return !is_null($this->ip_alias); + } + + public function toString(): string + { + return sprintf('%s:%s', $this->ip, $this->port); } /** * Gets information for the server associated with this allocation. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function server() + public function server(): BelongsTo { return $this->belongsTo(Server::class); } /** * Return the Node model associated with this allocation. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function node() + public function node(): BelongsTo { return $this->belongsTo(Node::class); } diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 797522c39..e141e89a2 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -2,52 +2,106 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Str; +use Webmozart\Assert\Assert; use Pterodactyl\Services\Acl\Api\AdminAcl; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\BelongsTo; -class ApiKey extends Model implements CleansAttributes, ValidableContract +/** + * Pterodactyl\Models\ApiKey. + * + * @property int $id + * @property int $user_id + * @property int $key_type + * @property string $identifier + * @property string $token + * @property array|null $allowed_ips + * @property string|null $memo + * @property \Illuminate\Support\Carbon|null $last_used_at + * @property \Illuminate\Support\Carbon|null $expires_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property int $r_servers + * @property int $r_nodes + * @property int $r_allocations + * @property int $r_users + * @property int $r_locations + * @property int $r_nests + * @property int $r_eggs + * @property int $r_database_hosts + * @property int $r_server_databases + * @property \Pterodactyl\Models\User $tokenable + * @property \Pterodactyl\Models\User $user + * + * @method static \Database\Factories\ApiKeyFactory factory(...$parameters) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey query() + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereAllowedIps($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereIdentifier($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereKeyType($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereLastUsedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereMemo($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRAllocations($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRDatabaseHosts($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereREggs($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRLocations($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRNests($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRNodes($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRServerDatabases($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRServers($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRUsers($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereToken($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUserId($value) + * + * @mixin \Eloquent + */ +class ApiKey extends Model { - use Eloquence, Validable; - + /** + * The resource name for this model when it is transformed into an + * API representation using fractal. + */ + public const RESOURCE_NAME = 'api_key'; /** * Different API keys that can exist on the system. */ - const TYPE_NONE = 0; - const TYPE_ACCOUNT = 1; - const TYPE_APPLICATION = 2; - const TYPE_DAEMON_USER = 3; - const TYPE_DAEMON_APPLICATION = 4; - + public const TYPE_NONE = 0; + public const TYPE_ACCOUNT = 1; + /* @deprecated */ + public const TYPE_APPLICATION = 2; + /* @deprecated */ + public const TYPE_DAEMON_USER = 3; + /* @deprecated */ + public const TYPE_DAEMON_APPLICATION = 4; /** * The length of API key identifiers. */ - const IDENTIFIER_LENGTH = 16; - + public const IDENTIFIER_LENGTH = 16; /** * The length of the actual API key that is encrypted and stored * in the database. */ - const KEY_LENGTH = 32; + public const KEY_LENGTH = 32; /** * The table associated with the model. - * - * @var string */ protected $table = 'api_keys'; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ - 'allowed_ips' => 'json', + 'allowed_ips' => 'array', 'user_id' => 'int', + 'last_used_at' => 'datetime', + 'expires_at' => 'datetime', + self::CREATED_AT => 'datetime', + self::UPDATED_AT => 'datetime', 'r_' . AdminAcl::RESOURCE_USERS => 'int', 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'int', 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'int', @@ -56,14 +110,11 @@ class ApiKey extends Model implements CleansAttributes, ValidableContract 'r_' . AdminAcl::RESOURCE_LOCATIONS => 'int', 'r_' . AdminAcl::RESOURCE_NESTS => 'int', 'r_' . AdminAcl::RESOURCE_NODES => 'int', - 'r_' . AdminAcl::RESOURCE_PACKS => 'int', 'r_' . AdminAcl::RESOURCE_SERVERS => 'int', ]; /** * Fields that are mass assignable. - * - * @var array */ protected $fillable = [ 'identifier', @@ -71,42 +122,28 @@ class ApiKey extends Model implements CleansAttributes, ValidableContract 'allowed_ips', 'memo', 'last_used_at', + 'expires_at', ]; /** * Fields that should not be included when calling toArray() or toJson() * on this model. - * - * @var array */ protected $hidden = ['token']; - /** - * Rules defining what fields must be passed when making a model. - * - * @var array - */ - protected static $applicationRules = [ - 'identifier' => 'required', - 'memo' => 'required', - 'user_id' => 'required', - 'token' => 'required', - 'key_type' => 'present', - ]; - /** * Rules to protect against invalid data entry to DB. - * - * @var array */ - protected static $dataIntegrityRules = [ - 'user_id' => 'exists:users,id', - 'key_type' => 'integer|min:0|max:4', - 'identifier' => 'string|size:16|unique:api_keys,identifier', - 'token' => 'string', - 'memo' => 'nullable|string|max:500', - 'allowed_ips' => 'nullable|json', + public static array $validationRules = [ + 'user_id' => 'required|exists:users,id', + 'key_type' => 'present|integer|min:0|max:4', + 'identifier' => 'required|string|size:16|unique:api_keys,identifier', + 'token' => 'required|string', + 'memo' => 'required|nullable|string|max:500', + 'allowed_ips' => 'nullable|array', + 'allowed_ips.*' => 'string', 'last_used_at' => 'nullable|date', + 'expires_at' => 'nullable|date', 'r_' . AdminAcl::RESOURCE_USERS => 'integer|min:0|max:3', 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'integer|min:0|max:3', 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'integer|min:0|max:3', @@ -115,16 +152,59 @@ class ApiKey extends Model implements CleansAttributes, ValidableContract 'r_' . AdminAcl::RESOURCE_LOCATIONS => 'integer|min:0|max:3', 'r_' . AdminAcl::RESOURCE_NESTS => 'integer|min:0|max:3', 'r_' . AdminAcl::RESOURCE_NODES => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_PACKS => 'integer|min:0|max:3', 'r_' . AdminAcl::RESOURCE_SERVERS => 'integer|min:0|max:3', ]; /** - * @var array + * Returns the user this token is assigned to. */ - protected $dates = [ - self::CREATED_AT, - self::UPDATED_AT, - 'last_used_at', - ]; + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * Required for support with Laravel Sanctum. + * + * @see \Laravel\Sanctum\Guard::supportsTokens() + */ + public function tokenable(): BelongsTo + { + return $this->user(); + } + + /** + * Finds the model matching the provided token. + */ + public static function findToken(string $token): ?self + { + $identifier = substr($token, 0, self::IDENTIFIER_LENGTH); + + $model = static::where('identifier', $identifier)->first(); + if (!is_null($model) && decrypt($model->token) === substr($token, strlen($identifier))) { + return $model; + } + + return null; + } + + /** + * Returns the standard prefix for API keys in the system. + */ + public static function getPrefixForType(int $type): string + { + Assert::oneOf($type, [self::TYPE_ACCOUNT, self::TYPE_APPLICATION]); + + return $type === self::TYPE_ACCOUNT ? 'ptlc_' : 'ptla_'; + } + + /** + * Generates a new identifier for an API key. + */ + public static function generateTokenIdentifier(int $type): string + { + $prefix = self::getPrefixForType($type); + + return $prefix . Str::random(self::IDENTIFIER_LENGTH - strlen($prefix)); + } } diff --git a/app/Models/Backup.php b/app/Models/Backup.php new file mode 100644 index 000000000..44fccf73f --- /dev/null +++ b/app/Models/Backup.php @@ -0,0 +1,75 @@ + 'int', + 'is_successful' => 'bool', + 'is_locked' => 'bool', + 'ignored_files' => 'array', + 'bytes' => 'int', + 'completed_at' => 'datetime', + ]; + + protected $attributes = [ + 'is_successful' => false, + 'is_locked' => false, + 'checksum' => null, + 'bytes' => 0, + 'upload_id' => null, + ]; + + protected $guarded = ['id', 'created_at', 'updated_at', 'deleted_at']; + + public static array $validationRules = [ + 'server_id' => 'bail|required|numeric|exists:servers,id', + 'uuid' => 'required|uuid', + 'is_successful' => 'boolean', + 'is_locked' => 'boolean', + 'name' => 'required|string', + 'ignored_files' => 'array', + 'disk' => 'required|string', + 'checksum' => 'nullable|string', + 'bytes' => 'numeric', + 'upload_id' => 'nullable|string', + ]; + + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } +} diff --git a/app/Models/DaemonKey.php b/app/Models/DaemonKey.php deleted file mode 100644 index c4c2940a9..000000000 --- a/app/Models/DaemonKey.php +++ /dev/null @@ -1,93 +0,0 @@ - 'integer', - 'server_id' => 'integer', - ]; - - /** - * @var array - */ - protected $dates = [ - self::CREATED_AT, - self::UPDATED_AT, - 'expires_at', - ]; - - /** - * @var array - */ - protected $fillable = ['user_id', 'server_id', 'secret', 'expires_at']; - - /** - * @var array - */ - protected static $applicationRules = [ - 'user_id' => 'required', - 'server_id' => 'required', - 'secret' => 'required', - 'expires_at' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'user_id' => 'numeric|exists:users,id', - 'server_id' => 'numeric|exists:servers,id', - 'secret' => 'string|min:20', - 'expires_at' => 'date', - ]; - - /** - * Return the server relation. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function server() - { - return $this->belongsTo(Server::class); - } - - /** - * Return the node relation. - * - * @return \Znck\Eloquent\Relations\BelongsToThrough - * @throws \Exception - */ - public function node() - { - return $this->belongsToThrough(Node::class, Server::class); - } - - /** - * Return the user relation. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo(User::class); - } -} diff --git a/app/Models/Database.php b/app/Models/Database.php index 9ff1d8c17..e3856a4e2 100644 --- a/app/Models/Database.php +++ b/app/Models/Database.php @@ -2,87 +2,108 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Container\Container; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Pterodactyl\Contracts\Extensions\HashidsInterface; -class Database extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property int $server_id + * @property int $database_host_id + * @property string $database + * @property string $username + * @property string $remote + * @property string $password + * @property int $max_connections + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Pterodactyl\Models\Server $server + * @property \Pterodactyl\Models\DatabaseHost $host + */ +class Database extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'server_database'; + public const RESOURCE_NAME = 'server_database'; /** * The table associated with the model. - * - * @var string */ protected $table = 'databases'; /** * The attributes excluded from the model's JSON form. - * - * @var array */ protected $hidden = ['password']; /** * Fields that are mass assignable. - * - * @var array */ protected $fillable = [ - 'server_id', 'database_host_id', 'database', 'username', 'password', 'remote', + 'server_id', 'database_host_id', 'database', 'username', 'password', 'remote', 'max_connections', ]; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'server_id' => 'integer', 'database_host_id' => 'integer', + 'max_connections' => 'integer', ]; - protected static $applicationRules = [ - 'server_id' => 'required', - 'database_host_id' => 'required', - 'database' => 'required', - 'remote' => 'required', - ]; - - protected static $dataIntegrityRules = [ - 'server_id' => 'numeric|exists:servers,id', - 'database_host_id' => 'exists:database_hosts,id', - 'database' => 'string|alpha_dash|between:3,100', + public static array $validationRules = [ + 'server_id' => 'required|numeric|exists:servers,id', + 'database_host_id' => 'required|exists:database_hosts,id', + 'database' => 'required|string|alpha_dash|between:3,48', 'username' => 'string|alpha_dash|between:3,100', - 'remote' => 'string|regex:/^[0-9%.]{1,15}$/', + 'max_connections' => 'nullable|integer', + 'remote' => 'required|string|regex:/^[\w\-\/.%:]+$/', 'password' => 'string', ]; /** - * Gets the host database server associated with a database. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * {@inheritDoc} */ - public function host() + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + + /** + * Resolves the database using the ID by checking if the value provided is a HashID + * string value, or just the ID to the database itself. + * + * @param mixed $value + * @param string|null $field + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function resolveRouteBinding($value, $field = null): ?\Illuminate\Database\Eloquent\Model + { + if (is_scalar($value) && ($field ?? $this->getRouteKeyName()) === 'id') { + $value = ctype_digit((string) $value) + ? $value + : Container::getInstance()->make(HashidsInterface::class)->decodeFirst($value); + } + + return $this->where($field ?? $this->getRouteKeyName(), $value)->firstOrFail(); + } + + /** + * Gets the host database server associated with a database. + */ + public function host(): BelongsTo { return $this->belongsTo(DatabaseHost::class, 'database_host_id'); } /** * Gets the server associated with a database. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function server() + public function server(): BelongsTo { return $this->belongsTo(Server::class); } diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php index f48977b1b..19a46333e 100644 --- a/app/Models/DatabaseHost.php +++ b/app/Models/DatabaseHost.php @@ -2,100 +2,79 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; -class DatabaseHost extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property string $name + * @property string $host + * @property int $port + * @property string $username + * @property string $password + * @property int|null $max_databases + * @property \Carbon\CarbonImmutable $created_at + * @property \Carbon\CarbonImmutable $updated_at + */ +class DatabaseHost extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'database_host'; + public const RESOURCE_NAME = 'database_host'; + + protected bool $immutableDates = true; /** * The table associated with the model. - * - * @var string */ protected $table = 'database_hosts'; /** * The attributes excluded from the model's JSON form. - * - * @var array */ protected $hidden = ['password']; /** * Fields that are mass assignable. - * - * @var array */ protected $fillable = [ - 'name', 'host', 'port', 'username', 'password', 'max_databases', 'node_id', + 'name', 'host', 'port', 'username', 'password', 'max_databases', ]; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'id' => 'integer', 'max_databases' => 'integer', - 'node_id' => 'integer', - ]; - - /** - * Application validation rules. - * - * @var array - */ - protected static $applicationRules = [ - 'name' => 'required', - 'host' => 'required', - 'port' => 'required', - 'username' => 'required', - 'node_id' => 'sometimes', ]; /** * Validation rules to assign to this model. - * - * @var array */ - protected static $dataIntegrityRules = [ - 'name' => 'string|max:255', - 'host' => 'unique:database_hosts,host', - 'port' => 'numeric|between:1,65535', - 'username' => 'string|max:32', + public static array $validationRules = [ + 'name' => 'required|string|max:191', + 'host' => 'required|string', + 'port' => 'required|numeric|between:1,65535', + 'username' => 'required|string|max:32', 'password' => 'nullable|string', - 'node_id' => 'nullable|integer|exists:nodes,id', ]; - /** - * Gets the node associated with a database host. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function node() - { - return $this->belongsTo(Node::class); - } - /** * Gets the databases associated with this host. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function databases() + public function databases(): HasMany { return $this->hasMany(Database::class); } + + /** + * Returns the nodes that a database host is assigned to. + */ + public function nodes(): BelongsToMany + { + return $this->belongsToMany(Node::class); + } } diff --git a/app/Models/Egg.php b/app/Models/Egg.php index ade28239d..d81623486 100644 --- a/app/Models/Egg.php +++ b/app/Models/Egg.php @@ -2,41 +2,93 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; -class Egg extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property string $uuid + * @property int $nest_id + * @property string $author + * @property string $name + * @property string|null $description + * @property array|null $features + * @property string $docker_image -- deprecated, use $docker_images + * @property array $docker_images + * @property string $update_url + * @property bool $force_outgoing_ip + * @property array|null $file_denylist + * @property string|null $config_files + * @property string|null $config_startup + * @property string|null $config_stop + * @property int|null $config_from + * @property string|null $startup + * @property bool $script_is_privileged + * @property string|null $script_install + * @property ?string $script_entry + * @property ?string $script_container + * @property ?int $copy_script_from + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string|null $copy_script_install + * @property string $copy_script_entry + * @property string $copy_script_container + * @property string|null $inherit_config_files + * @property string|null $inherit_config_startup + * @property string|null $inherit_config_stop + * @property string $inherit_file_denylist + * @property array|null $inherit_features + * @property \Pterodactyl\Models\Nest $nest + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Server[] $servers + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\EggVariable[] $variables + * @property \Pterodactyl\Models\Egg|null $scriptFrom + * @property \Pterodactyl\Models\Egg|null $configFrom + */ +class Egg extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'egg'; + public const RESOURCE_NAME = 'egg'; + + /** + * Defines the current egg export version. + */ + public const EXPORT_VERSION = 'PTDL_v2'; + + /** + * Different features that can be enabled on any given egg. These are used internally + * to determine which types of frontend functionality should be shown to the user. Eggs + * will automatically inherit features from a parent egg if they are already configured + * to copy configuration values from said egg. + * + * To skip copying the features, an empty array value should be passed in ("[]") rather + * than leaving it null. + */ + public const FEATURE_EULA_POPUP = 'eula'; + public const FEATURE_FASTDL = 'fastdl'; /** * The table associated with the model. - * - * @var string */ protected $table = 'eggs'; /** * Fields that are not mass assignable. - * - * @var array */ protected $fillable = [ + 'nest_id', + 'author', + 'uuid', 'name', 'description', - 'docker_image', + 'features', + 'docker_images', + 'force_outgoing_ip', + 'file_denylist', 'config_files', 'config_startup', - 'config_logs', 'config_stop', 'config_from', 'startup', @@ -49,71 +101,54 @@ class Egg extends Model implements CleansAttributes, ValidableContract /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'nest_id' => 'integer', 'config_from' => 'integer', 'script_is_privileged' => 'boolean', + 'force_outgoing_ip' => 'boolean', 'copy_script_from' => 'integer', + 'features' => 'array', + 'docker_images' => 'array', + 'file_denylist' => 'array', ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'nest_id' => 'required', - 'uuid' => 'required', - 'name' => 'required', - 'description' => 'required', - 'author' => 'required', - 'docker_image' => 'required', - 'startup' => 'required', - 'config_from' => 'sometimes', - 'config_stop' => 'required_without:config_from', - 'config_startup' => 'required_without:config_from', - 'config_logs' => 'required_without:config_from', - 'config_files' => 'required_without:config_from', + public static array $validationRules = [ + 'nest_id' => 'required|bail|numeric|exists:nests,id', + 'uuid' => 'required|string|size:36', + 'name' => 'required|string|max:191', + 'description' => 'string|nullable', + 'features' => 'array|nullable', + 'author' => 'required|string|email', + 'file_denylist' => 'array|nullable', + 'file_denylist.*' => 'string', + 'docker_images' => 'required|array|min:1', + 'docker_images.*' => 'required|string', + 'startup' => 'required|nullable|string', + 'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id', + 'config_stop' => 'required_without:config_from|nullable|string|max:191', + 'config_startup' => 'required_without:config_from|nullable|json', + 'config_files' => 'required_without:config_from|nullable|json', + 'update_url' => 'sometimes|nullable|string', + 'force_outgoing_ip' => 'sometimes|boolean', ]; - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'nest_id' => 'bail|numeric|exists:nests,id', - 'uuid' => 'string|size:36', - 'name' => 'string|max:255', - 'description' => 'string', - 'author' => 'string|email', - 'docker_image' => 'string|max:255', - 'startup' => 'nullable|string', - 'config_from' => 'bail|nullable|numeric|exists:eggs,id', - 'config_stop' => 'nullable|string|max:255', - 'config_startup' => 'nullable|json', - 'config_logs' => 'nullable|json', - 'config_files' => 'nullable|json', - ]; - - /** - * @var array - */ protected $attributes = [ + 'features' => null, + 'file_denylist' => null, 'config_stop' => null, 'config_startup' => null, - 'config_logs' => null, 'config_files' => null, + 'update_url' => null, ]; /** * Returns the install script for the egg; if egg is copying from another * it will return the copied script. - * - * @return string */ - public function getCopyScriptInstallAttribute() + public function getCopyScriptInstallAttribute(): ?string { - if (! is_null($this->script_install) || is_null($this->copy_script_from)) { + if (!is_null($this->script_install) || is_null($this->copy_script_from)) { return $this->script_install; } @@ -123,12 +158,10 @@ class Egg extends Model implements CleansAttributes, ValidableContract /** * Returns the entry command for the egg; if egg is copying from another * it will return the copied entry command. - * - * @return string */ - public function getCopyScriptEntryAttribute() + public function getCopyScriptEntryAttribute(): string { - if (! is_null($this->script_entry) || is_null($this->copy_script_from)) { + if (!is_null($this->script_entry) || is_null($this->copy_script_from)) { return $this->script_entry; } @@ -138,12 +171,10 @@ class Egg extends Model implements CleansAttributes, ValidableContract /** * Returns the install container for the egg; if egg is copying from another * it will return the copied install container. - * - * @return string */ - public function getCopyScriptContainerAttribute() + public function getCopyScriptContainerAttribute(): string { - if (! is_null($this->script_container) || is_null($this->copy_script_from)) { + if (!is_null($this->script_container) || is_null($this->copy_script_from)) { return $this->script_container; } @@ -152,12 +183,10 @@ class Egg extends Model implements CleansAttributes, ValidableContract /** * Return the file configuration for an egg. - * - * @return string */ - public function getInheritConfigFilesAttribute() + public function getInheritConfigFilesAttribute(): ?string { - if (! is_null($this->config_files) || is_null($this->config_from)) { + if (!is_null($this->config_files) || is_null($this->config_from)) { return $this->config_files; } @@ -166,40 +195,22 @@ class Egg extends Model implements CleansAttributes, ValidableContract /** * Return the startup configuration for an egg. - * - * @return string */ - public function getInheritConfigStartupAttribute() + public function getInheritConfigStartupAttribute(): ?string { - if (! is_null($this->config_startup) || is_null($this->config_from)) { + if (!is_null($this->config_startup) || is_null($this->config_from)) { return $this->config_startup; } return $this->configFrom->config_startup; } - /** - * Return the log reading configuration for an egg. - * - * @return string - */ - public function getInheritConfigLogsAttribute() - { - if (! is_null($this->config_logs) || is_null($this->config_from)) { - return $this->config_logs; - } - - return $this->configFrom->config_logs; - } - /** * Return the stop command configuration for an egg. - * - * @return string */ - public function getInheritConfigStopAttribute() + public function getInheritConfigStopAttribute(): ?string { - if (! is_null($this->config_stop) || is_null($this->config_from)) { + if (!is_null($this->config_stop) || is_null($this->config_from)) { return $this->config_stop; } @@ -207,61 +218,67 @@ class Egg extends Model implements CleansAttributes, ValidableContract } /** - * Gets nest associated with an egg. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * Returns the features available to this egg from the parent configuration if there are + * no features defined for this egg specifically and there is a parent egg configured. */ - public function nest() + public function getInheritFeaturesAttribute(): ?array + { + if (!is_null($this->features) || is_null($this->config_from)) { + return $this->features; + } + + return $this->configFrom->features; + } + + /** + * Returns the features available to this egg from the parent configuration if there are + * no features defined for this egg specifically and there is a parent egg configured. + */ + public function getInheritFileDenylistAttribute(): ?array + { + if (is_null($this->config_from)) { + return $this->file_denylist; + } + + return $this->configFrom->file_denylist; + } + + /** + * Gets nest associated with an egg. + */ + public function nest(): BelongsTo { return $this->belongsTo(Nest::class); } /** * Gets all servers associated with this egg. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function servers() + public function servers(): HasMany { return $this->hasMany(Server::class, 'egg_id'); } /** * Gets all variables associated with this egg. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function variables() + public function variables(): HasMany { return $this->hasMany(EggVariable::class, 'egg_id'); } - /** - * Gets all packs associated with this egg. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function packs() - { - return $this->hasMany(Pack::class, 'egg_id'); - } - /** * Get the parent egg from which to copy scripts. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function scriptFrom() + public function scriptFrom(): BelongsTo { return $this->belongsTo(self::class, 'copy_script_from'); } /** * Get the parent egg from which to copy configuration settings. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function configFrom() + public function configFrom(): BelongsTo { return $this->belongsTo(self::class, 'config_from'); } diff --git a/app/Models/EggMount.php b/app/Models/EggMount.php new file mode 100644 index 000000000..ed494a13e --- /dev/null +++ b/app/Models/EggMount.php @@ -0,0 +1,12 @@ + 'integer', - 'user_viewable' => 'integer', - 'user_editable' => 'integer', + 'user_viewable' => 'bool', + 'user_editable' => 'bool', ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'name' => 'required', - 'env_variable' => 'required', - 'rules' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ + public static array $validationRules = [ 'egg_id' => 'exists:eggs,id', - 'name' => 'string|between:1,255', + 'name' => 'required|string|between:1,191', 'description' => 'string', - 'env_variable' => 'regex:/^[\w]{1,255}$/|notIn:' . self::RESERVED_ENV_NAMES, + 'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . self::RESERVED_ENV_NAMES, 'default_value' => 'string', 'user_viewable' => 'boolean', 'user_editable' => 'boolean', - 'rules' => 'string', + 'rules' => 'required|string', ]; - /** - * @var array - */ protected $attributes = [ 'user_editable' => 0, 'user_viewable' => 0, ]; - /** - * @param $value - * @return bool - */ - public function getRequiredAttribute($value) + public function getRequiredAttribute(): bool { - return $this->rules === 'required' || str_contains($this->rules, ['required|', '|required']); + return in_array('required', explode('|', $this->rules)); + } + + /** + * Returns the egg that this variable belongs to. + */ + public function egg(): BelongsTo + { + return $this->belongsTo(Egg::class); } /** * Return server variables associated with this variable. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function serverVariable() + public function serverVariables(): HasMany { return $this->hasMany(ServerVariable::class, 'variable_id'); } diff --git a/app/Models/Filters/AdminServerFilter.php b/app/Models/Filters/AdminServerFilter.php new file mode 100644 index 000000000..638946734 --- /dev/null +++ b/app/Models/Filters/AdminServerFilter.php @@ -0,0 +1,35 @@ +getQuery()->from !== 'servers') { + throw new \BadMethodCallException('Cannot use the AdminServerFilter against a non-server model.'); + } + $query + ->select('servers.*') + ->leftJoin('users', 'users.id', '=', 'servers.owner_id') + ->where(function (Builder $builder) use ($value) { + $builder->where('servers.uuid', $value) + ->orWhere('servers.uuid', 'LIKE', "$value%") + ->orWhere('servers.uuidShort', $value) + ->orWhere('servers.external_id', $value) + ->orWhereRaw('LOWER(users.username) LIKE ?', ["%$value%"]) + ->orWhereRaw('LOWER(users.email) LIKE ?', ["$value%"]) + ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]); + }) + ->groupBy('servers.id'); + } +} diff --git a/app/Models/Filters/MultiFieldServerFilter.php b/app/Models/Filters/MultiFieldServerFilter.php new file mode 100644 index 000000000..fe0a7ebd5 --- /dev/null +++ b/app/Models/Filters/MultiFieldServerFilter.php @@ -0,0 +1,69 @@ +getQuery()->from !== 'servers') { + throw new \BadMethodCallException('Cannot use the MultiFieldServerFilter against a non-server model.'); + } + + if (preg_match(self::IPV4_REGEX, $value) || preg_match('/^:\d{1,5}$/', $value)) { + $query + // Only select the server values, otherwise you'll end up merging the allocation and + // server objects together, resulting in incorrect behavior and returned values. + ->select('servers.*') + ->join('allocations', 'allocations.server_id', '=', 'servers.id') + ->where(function (Builder $builder) use ($value) { + $parts = explode(':', $value); + + $builder->when( + !Str::startsWith($value, ':'), + // When the string does not start with a ":" it means we're looking for an IP or IP:Port + // combo, so use a query to handle that. + function (Builder $builder) use ($parts) { + $builder->orWhere('allocations.ip', $parts[0]); + if (!is_null($parts[1] ?? null)) { + $builder->where('allocations.port', 'LIKE', "{$parts[1]}%"); + } + }, + // Otherwise, just try to search for that specific port in the allocations. + function (Builder $builder) use ($value) { + $builder->orWhere('allocations.port', 'LIKE', substr($value, 1) . '%'); + } + ); + }) + ->groupBy('servers.id'); + + return; + } + + $query + ->where(function (Builder $builder) use ($value) { + $builder->where('servers.uuid', $value) + ->orWhere('servers.uuid', 'LIKE', "$value%") + ->orWhere('servers.uuidShort', $value) + ->orWhere('servers.external_id', $value) + ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]); + }); + } +} diff --git a/app/Models/Location.php b/app/Models/Location.php index 10fff147c..47a30354b 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -2,72 +2,64 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; -class Location extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property string $short + * @property string $long + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Pterodactyl\Models\Node[] $nodes + * @property \Pterodactyl\Models\Server[] $servers + */ +class Location extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'location'; + public const RESOURCE_NAME = 'location'; /** * The table associated with the model. - * - * @var string */ protected $table = 'locations'; /** * Fields that are not mass assignable. - * - * @var array */ protected $guarded = ['id', 'created_at', 'updated_at']; /** - * Validation rules to apply to this model. - * - * @var array + * Rules ensuring that the raw data stored in the database meets expectations. */ - protected static $applicationRules = [ - 'short' => 'required', - 'long' => 'required', + public static array $validationRules = [ + 'short' => 'required|string|between:1,60|unique:locations,short', + 'long' => 'string|nullable|between:1,191', ]; /** - * Rules ensuring that the raw data stored in the database meets expectations. - * - * @var array + * {@inheritDoc} */ - protected static $dataIntegrityRules = [ - 'short' => 'string|between:1,60|unique:locations,short', - 'long' => 'string|between:1,255', - ]; + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } /** * Gets the nodes in a specified location. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function nodes() + public function nodes(): HasMany { return $this->hasMany(Node::class); } /** * Gets the servers within a given location. - * - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough */ - public function servers() + public function servers(): HasManyThrough { return $this->hasManyThrough(Server::class, Node::class); } diff --git a/app/Models/Model.php b/app/Models/Model.php new file mode 100644 index 000000000..34b856c38 --- /dev/null +++ b/app/Models/Model.php @@ -0,0 +1,188 @@ +make(ValidationFactory::class); + + static::saving(function (Model $model) { + try { + $model->validate(); + } catch (ValidationException $exception) { + throw new DataValidationException($exception->validator, $model); + } + + return true; + }); + } + + /** + * Returns the model key to use for route model binding. By default, we'll + * assume every model uses a UUID field for this. If the model does not have + * a UUID and is using a different key it should be specified on the model + * itself. + * + * You may also optionally override this on a per-route basis by declaring + * the key name in the URL definition, like "{user:id}". + */ + public function getRouteKeyName(): string + { + return 'uuid'; + } + + /** + * Set the model to skip validation when saving. + */ + public function skipValidation(): self + { + $this->skipValidation = true; + + return $this; + } + + /** + * Returns the validator instance used by this model. + */ + public function getValidator(): Validator + { + $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); + + return static::$validatorFactory->make([], $rules, [], []); + } + + /** + * Returns the rules associated with this model. + */ + public static function getRules(): array + { + $rules = static::$validationRules; + foreach ($rules as $key => &$rule) { + $rule = is_array($rule) ? $rule : explode('|', $rule); + } + + return $rules; + } + + /** + * Returns the rules for a specific field. If the field is not found an empty + * array is returned. + */ + public static function getRulesForField(string $field): array + { + return Arr::get(static::getRules(), $field) ?? []; + } + + /** + * Returns the rules associated with the model, specifically for updating the given model + * rather than just creating it. + */ + public static function getRulesForUpdate($model, string $column = 'id'): array + { + if ($model instanceof Model) { + [$id, $column] = [$model->getKey(), $model->getKeyName()]; + } + + $rules = static::getRules(); + foreach ($rules as $key => &$data) { + // For each rule in a given field, iterate over it and confirm if the rule + // is one for a unique field. If that is the case, append the ID of the current + // working model, so we don't run into errors due to the way that field validation + // works. + foreach ($data as &$datum) { + if (!is_string($datum) || !Str::startsWith($datum, 'unique')) { + continue; + } + + [, $args] = explode(':', $datum); + $args = explode(',', $args); + + $datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column); + } + } + + return $rules; + } + + /** + * Determines if the model is in a valid state or not. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function validate(): void + { + if ($this->skipValidation) { + return; + } + + /** @var \Illuminate\Validation\Validator $validator */ + $validator = $this->getValidator(); + $validator->setData( + // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist + // for that model. Doing this will return all the attributes in a format that can + // properly be validated. + $this->addCastAttributesToArray( + $this->getAttributes(), + $this->getMutatedAttributes() + ) + ); + + if (!$validator->passes()) { + throw new ValidationException($validator); + } + } + + /** + * Return a timestamp as DateTime object. + * + * @param mixed $value + */ + protected function asDateTime($value): Carbon|CarbonImmutable + { + if (!$this->immutableDates) { + return parent::asDateTime($value); + } + + return parent::asDateTime($value)->toImmutable(); + } +} diff --git a/app/Models/Mount.php b/app/Models/Mount.php new file mode 100644 index 000000000..475b3cb7f --- /dev/null +++ b/app/Models/Mount.php @@ -0,0 +1,118 @@ + 'int', + 'read_only' => 'bool', + 'user_mountable' => 'bool', + ]; + + /** + * Rules verifying that the data being stored matches the expectations of the database. + */ + public static array $validationRules = [ + 'name' => 'required|string|min:2|max:64|unique:mounts,name', + 'description' => 'nullable|string|max:191', + 'source' => 'required|string', + 'target' => 'required|string', + 'read_only' => 'sometimes|boolean', + 'user_mountable' => 'sometimes|boolean', + ]; + + /** + * Implement language verification by overriding Eloquence's gather + * rules function. + */ + public static function getRules(): array + { + $rules = parent::getRules(); + + $rules['source'][] = new NotIn(Mount::$invalidSourcePaths); + $rules['target'][] = new NotIn(Mount::$invalidTargetPaths); + + return $rules; + } + + /** + * Disable timestamps on this model. + */ + public $timestamps = false; + + /** + * Blacklisted source paths. + */ + public static $invalidSourcePaths = [ + '/etc/pterodactyl', + '/var/lib/pterodactyl/volumes', + '/srv/daemon-data', + ]; + + /** + * Blacklisted target paths. + */ + public static $invalidTargetPaths = [ + '/home/container', + ]; + + /** + * Returns all eggs that have this mount assigned. + */ + public function eggs(): BelongsToMany + { + return $this->belongsToMany(Egg::class); + } + + /** + * Returns all nodes that have this mount assigned. + */ + public function nodes(): BelongsToMany + { + return $this->belongsToMany(Node::class); + } + + /** + * Returns all servers that have this mount assigned. + */ + public function servers(): BelongsToMany + { + return $this->belongsToMany(Server::class); + } +} diff --git a/app/Models/MountNode.php b/app/Models/MountNode.php new file mode 100644 index 000000000..3a189c46b --- /dev/null +++ b/app/Models/MountNode.php @@ -0,0 +1,14 @@ + 'required', - 'name' => 'required', - 'description' => 'sometimes', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'author' => 'string|email', - 'name' => 'string|max:255', + public static array $validationRules = [ + 'author' => 'required|string|email', + 'name' => 'required|string|max:191', 'description' => 'nullable|string', ]; /** * Gets all eggs associated with this service. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function eggs() + public function eggs(): HasMany { return $this->hasMany(Egg::class); } - /** - * Returns all of the packs associated with a nest, regardless of the egg. - * - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough - */ - public function packs() - { - return $this->hasManyThrough(Pack::class, Egg::class, 'nest_id', 'egg_id'); - } - /** * Gets all servers associated with this nest. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function servers() + public function servers(): HasMany { return $this->hasMany(Server::class); } diff --git a/app/Models/Node.php b/app/Models/Node.php index 1a412be95..1ab72a5bd 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -2,50 +2,92 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Str; +use Symfony\Component\Yaml\Yaml; +use Illuminate\Container\Container; use Illuminate\Notifications\Notifiable; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Contracts\Encryption\Encrypter; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; -class Node extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property string $uuid + * @property bool $public + * @property string $name + * @property string|null $description + * @property int $location_id + * @property int|null $database_host_id + * @property string $scheme + * @property string $fqdn + * @property int $listen_port_http + * @property int $listen_port_sftp + * @property int $public_port_http + * @property int $public_port_sftp + * @property bool $behind_proxy + * @property bool $maintenance_mode + * @property int $memory + * @property int $memory_overallocate + * @property int $sum_memory + * @property int $disk + * @property int $disk_overallocate + * @property int $sum_disk + * @property int $upload_size + * @property string $daemon_token_id + * @property string $daemon_token + * @property string $daemon_base + * @property int $servers_count + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property Allocation[]|Collection $allocations + * @property \Pterodactyl\Models\DatabaseHost|null $databaseHost + * @property Location $location + * @property Mount[]|Collection $mounts + * @property int[]|\Illuminate\Support\Collection $ports + * @property Server[]|Collection $servers + */ +class Node extends Model { - use Eloquence, Notifiable, Validable; + use Notifiable; /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'node'; + public const RESOURCE_NAME = 'node'; - const DAEMON_SECRET_LENGTH = 36; + /** + * The default location of server files on the Wings instance. + */ + public const DEFAULT_DAEMON_BASE = '/var/lib/pterodactyl/volumes'; + + public const DAEMON_TOKEN_ID_LENGTH = 16; + public const DAEMON_TOKEN_LENGTH = 64; /** * The table associated with the model. - * - * @var string */ protected $table = 'nodes'; /** * The attributes excluded from the model's JSON form. - * - * @var array */ - protected $hidden = ['daemonSecret']; + protected $hidden = ['daemon_token_id', 'daemon_token']; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'location_id' => 'integer', + 'database_host_id' => 'integer', + 'listen_port_http' => 'integer', + 'listen_port_sftp' => 'integer', + 'public_port_http' => 'integer', + 'public_port_sftp' => 'integer', 'memory' => 'integer', 'disk' => 'integer', - 'daemonListen' => 'integer', - 'daemonSFTP' => 'integer', 'behind_proxy' => 'boolean', 'public' => 'boolean', 'maintenance_mode' => 'boolean', @@ -53,182 +95,182 @@ class Node extends Model implements CleansAttributes, ValidableContract /** * Fields that are mass assignable. - * - * @var array */ protected $fillable = [ - 'public', 'name', 'location_id', + 'public', 'name', 'location_id', 'database_host_id', + 'listen_port_http', 'listen_port_sftp', 'public_port_http', 'public_port_sftp', 'fqdn', 'scheme', 'behind_proxy', 'memory', 'memory_overallocate', 'disk', - 'disk_overallocate', 'upload_size', - 'daemonSecret', 'daemonBase', - 'daemonSFTP', 'daemonListen', + 'disk_overallocate', 'upload_size', 'daemon_base', 'description', 'maintenance_mode', ]; - /** - * Fields that are searchable. - * - * @var array - */ - protected $searchableColumns = [ - 'name' => 10, - 'fqdn' => 8, - 'location.short' => 4, - 'location.long' => 4, - ]; - - /** - * @var array - */ - protected static $applicationRules = [ - 'name' => 'required', - 'location_id' => 'required', - 'fqdn' => 'required', - 'scheme' => 'required', - 'memory' => 'required', - 'memory_overallocate' => 'required', - 'disk' => 'required', - 'disk_overallocate' => 'required', - 'daemonBase' => 'sometimes|required', - 'daemonSFTP' => 'required', - 'daemonListen' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'name' => 'regex:/^([\w .-]{1,100})$/', - 'description' => 'string', - 'location_id' => 'exists:locations,id', + public static array $validationRules = [ + 'name' => 'required|regex:/^([\w .-]{1,100})$/', + 'description' => 'string|nullable', + 'location_id' => 'required|exists:locations,id', + 'database_host_id' => 'sometimes|nullable|exists:database_hosts,id', 'public' => 'boolean', - 'fqdn' => 'string', + 'fqdn' => 'required|string', + 'listen_port_http' => 'required|numeric|between:1,65535', + 'listen_port_sftp' => 'required|numeric|between:1,65535', + 'public_port_http' => 'required|numeric|between:1,65535', + 'public_port_sftp' => 'required|numeric|between:1,65535', + 'scheme' => 'required', 'behind_proxy' => 'boolean', - 'memory' => 'numeric|min:1', - 'memory_overallocate' => 'numeric|min:-1', - 'disk' => 'numeric|min:1', - 'disk_overallocate' => 'numeric|min:-1', - 'daemonBase' => 'regex:/^([\/][\d\w.\-\/]+)$/', - 'daemonSFTP' => 'numeric|between:1,65535', - 'daemonListen' => 'numeric|between:1,65535', + 'memory' => 'required|numeric|min:1', + 'memory_overallocate' => 'required|numeric|min:-1', + 'disk' => 'required|numeric|min:1', + 'disk_overallocate' => 'required|numeric|min:-1', + 'daemon_base' => 'sometimes|required|regex:/^([\/][\d\w.\-\/]+)$/', 'maintenance_mode' => 'boolean', 'upload_size' => 'int|between:1,1024', ]; /** * Default values for specific columns that are generally not changed on base installs. - * - * @var array */ protected $attributes = [ + 'listen_port_http' => 8080, + 'listen_port_sftp' => 2022, + 'public_port_http' => 8080, + 'public_port_sftp' => 2022, 'public' => true, 'behind_proxy' => false, 'memory_overallocate' => 0, 'disk_overallocate' => 0, - 'daemonBase' => '/srv/daemon-data', - 'daemonSFTP' => 2022, - 'daemonListen' => 8080, + 'daemon_base' => self::DEFAULT_DAEMON_BASE, 'maintenance_mode' => false, ]; /** - * Returns the configuration in JSON format. - * - * @param bool $pretty - * @return string + * Get the connection address to use when making calls to this node. */ - public function getConfigurationAsJson($pretty = false) + public function getConnectionAddress(): string { - $config = [ - 'web' => [ - 'host' => '0.0.0.0', - 'listen' => $this->daemonListen, - 'ssl' => [ - 'enabled' => (! $this->behind_proxy && $this->scheme === 'https'), - 'certificate' => '/etc/letsencrypt/live/' . $this->fqdn . '/fullchain.pem', - 'key' => '/etc/letsencrypt/live/' . $this->fqdn . '/privkey.pem', - ], - ], - 'docker' => [ - 'container' => [ - 'user' => null, - ], - 'network' => [ - 'name' => 'pterodactyl_nw', - ], - 'socket' => '/var/run/docker.sock', - 'autoupdate_images' => true, - ], - 'filesystem' => [ - 'server_logs' => '/tmp/pterodactyl', - ], - 'internals' => [ - 'disk_use_seconds' => 30, - 'set_permissions_on_boot' => true, - 'throttle' => [ - 'enabled' => true, - 'kill_at_count' => 5, - 'decay' => 10, - 'lines' => 1000, - 'check_interval_ms' => 100, - ], - ], - 'sftp' => [ - 'path' => $this->daemonBase, - 'ip' => '0.0.0.0', - 'port' => $this->daemonSFTP, - 'keypair' => [ - 'bits' => 2048, - 'e' => 65537, - ], - ], - 'logger' => [ - 'path' => 'logs/', - 'src' => false, - 'level' => 'info', - 'period' => '1d', - 'count' => 3, - ], - 'remote' => [ - 'base' => route('index'), - ], - 'uploads' => [ - 'size_limit' => $this->upload_size, - ], - 'keys' => [$this->daemonSecret], - ]; + return sprintf('%s://%s:%s', $this->scheme, $this->fqdn, $this->public_port_http); + } - return json_encode($config, ($pretty) ? JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT : JSON_UNESCAPED_SLASHES); + /** + * Returns the configuration as an array. + */ + public function getConfiguration(): array + { + return [ + 'debug' => false, + 'uuid' => $this->uuid, + 'token_id' => $this->daemon_token_id, + 'token' => Container::getInstance()->make(Encrypter::class)->decrypt($this->daemon_token), + 'api' => [ + 'host' => '0.0.0.0', + 'port' => $this->listen_port_http, + 'ssl' => [ + 'enabled' => (!$this->behind_proxy && $this->scheme === 'https'), + 'cert' => '/etc/letsencrypt/live/' . Str::lower($this->fqdn) . '/fullchain.pem', + 'key' => '/etc/letsencrypt/live/' . Str::lower($this->fqdn) . '/privkey.pem', + ], + 'upload_limit' => $this->upload_size, + ], + 'system' => [ + 'data' => $this->daemon_base, + 'sftp' => [ + 'bind_port' => $this->listen_port_sftp, + ], + ], + 'allowed_mounts' => $this->mounts->pluck('source')->toArray(), + 'remote' => route('index'), + ]; + } + + /** + * Returns the configuration in Yaml format. + */ + public function getYamlConfiguration(): string + { + return Yaml::dump($this->getConfiguration(), 4, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + } + + /** + * Returns the configuration in JSON format. + */ + public function getJsonConfiguration(bool $pretty = false): string + { + return json_encode($this->getConfiguration(), $pretty ? JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT : JSON_UNESCAPED_SLASHES); + } + + /** + * Helper function to return the decrypted key for a node. + */ + public function getDecryptedKey(): string + { + return (string) Container::getInstance()->make(Encrypter::class)->decrypt( + $this->daemon_token + ); + } + + public function isUnderMaintenance(): bool + { + return $this->maintenance_mode; + } + + /** + * Gets the allocations associated with a node. + */ + public function allocations(): HasMany + { + return $this->hasMany(Allocation::class); + } + + /** + * Returns the database host associated with a node. + */ + public function databaseHost(): BelongsTo + { + return $this->belongsTo(DatabaseHost::class); } /** * Gets the location associated with a node. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function location() + public function location(): BelongsTo { return $this->belongsTo(Location::class); } /** - * Gets the servers associated with a node. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * Returns a HasManyThrough relationship for all the mounts associated with a node. */ - public function servers() + public function mounts(): HasManyThrough + { + return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id'); + } + + /** + * Gets the servers associated with a node. + */ + public function servers(): HasMany { return $this->hasMany(Server::class); } - /** - * Gets the allocations associated with a node. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function allocations() + public function loadServerSums(): self { - return $this->hasMany(Allocation::class); + $this->loadSum('servers as sum_memory', 'memory'); + $this->loadSum('servers as sum_disk', 'disk'); + + return $this; + } + + /** + * Returns a boolean if the node is viable for an additional server to be placed on it. + */ + public function isViable(int $memory = 0, int $disk = 0): bool + { + $this->loadServerSums(); + + $memoryLimit = $this->memory * (1.0 + ($this->memory_overallocate / 100.0)); + $diskLimit = $this->disk * (1.0 + ($this->disk_overallocate / 100.0)); + + return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit; } } diff --git a/app/Models/Objects/DeploymentObject.php b/app/Models/Objects/DeploymentObject.php index 52857410f..0c44be08e 100644 --- a/app/Models/Objects/DeploymentObject.php +++ b/app/Models/Objects/DeploymentObject.php @@ -4,72 +4,42 @@ namespace Pterodactyl\Models\Objects; class DeploymentObject { - /** - * @var bool - */ - private $dedicated = false; + private bool $dedicated = false; - /** - * @var array - */ - private $locations = []; + private array $locations = []; - /** - * @var array - */ - private $ports = []; + private array $ports = []; - /** - * @return bool - */ public function isDedicated(): bool { return $this->dedicated; } - /** - * @param bool $dedicated - * @return $this - */ - public function setDedicated(bool $dedicated) + public function setDedicated(bool $dedicated): self { $this->dedicated = $dedicated; return $this; } - /** - * @return array - */ public function getLocations(): array { return $this->locations; } - /** - * @param array $locations - * @return $this - */ - public function setLocations(array $locations) + public function setLocations(array $locations): self { $this->locations = $locations; return $this; } - /** - * @return array - */ public function getPorts(): array { return $this->ports; } - /** - * @param array $ports - * @return $this - */ - public function setPorts(array $ports) + public function setPorts(array $ports): self { $this->ports = $ports; diff --git a/app/Models/Pack.php b/app/Models/Pack.php deleted file mode 100644 index 657d2f1d0..000000000 --- a/app/Models/Pack.php +++ /dev/null @@ -1,107 +0,0 @@ - 'required', - 'version' => 'required', - 'description' => 'sometimes', - 'selectable' => 'sometimes|required', - 'visible' => 'sometimes|required', - 'locked' => 'sometimes|required', - 'egg_id' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'name' => 'string', - 'version' => 'string', - 'description' => 'nullable|string', - 'selectable' => 'boolean', - 'visible' => 'boolean', - 'locked' => 'boolean', - 'egg_id' => 'exists:eggs,id', - ]; - - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'egg_id' => 'integer', - 'selectable' => 'boolean', - 'visible' => 'boolean', - 'locked' => 'boolean', - ]; - - /** - * Parameters for search querying. - * - * @var array - */ - protected $searchableColumns = [ - 'name' => 10, - 'uuid' => 8, - 'egg.name' => 6, - 'egg.docker_image' => 5, - 'version' => 2, - ]; - - /** - * Gets egg associated with a service pack. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function egg() - { - return $this->belongsTo(Egg::class); - } - - /** - * Gets servers associated with a pack. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function servers() - { - return $this->hasMany(Server::class); - } -} diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 5d9ea6c72..b2e00070e 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -2,163 +2,218 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Support\Collection; -class Permission extends Model implements CleansAttributes, ValidableContract +class Permission extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'subuser_permission'; + public const RESOURCE_NAME = 'subuser_permission'; + + /** + * Constants defining different permissions available. + */ + public const ACTION_WEBSOCKET_CONNECT = 'websocket.connect'; + public const ACTION_CONTROL_CONSOLE = 'control.console'; + public const ACTION_CONTROL_START = 'control.start'; + public const ACTION_CONTROL_STOP = 'control.stop'; + public const ACTION_CONTROL_RESTART = 'control.restart'; + + public const ACTION_DATABASE_READ = 'database.read'; + public const ACTION_DATABASE_CREATE = 'database.create'; + public const ACTION_DATABASE_UPDATE = 'database.update'; + public const ACTION_DATABASE_DELETE = 'database.delete'; + public const ACTION_DATABASE_VIEW_PASSWORD = 'database.view_password'; + + public const ACTION_SCHEDULE_READ = 'schedule.read'; + public const ACTION_SCHEDULE_CREATE = 'schedule.create'; + public const ACTION_SCHEDULE_UPDATE = 'schedule.update'; + public const ACTION_SCHEDULE_DELETE = 'schedule.delete'; + + public const ACTION_USER_READ = 'user.read'; + public const ACTION_USER_CREATE = 'user.create'; + public const ACTION_USER_UPDATE = 'user.update'; + public const ACTION_USER_DELETE = 'user.delete'; + + public const ACTION_BACKUP_READ = 'backup.read'; + public const ACTION_BACKUP_CREATE = 'backup.create'; + public const ACTION_BACKUP_DELETE = 'backup.delete'; + public const ACTION_BACKUP_DOWNLOAD = 'backup.download'; + public const ACTION_BACKUP_RESTORE = 'backup.restore'; + + public const ACTION_ALLOCATION_READ = 'allocation.read'; + public const ACTION_ALLOCATION_CREATE = 'allocation.create'; + public const ACTION_ALLOCATION_UPDATE = 'allocation.update'; + public const ACTION_ALLOCATION_DELETE = 'allocation.delete'; + + public const ACTION_FILE_READ = 'file.read'; + public const ACTION_FILE_READ_CONTENT = 'file.read-content'; + public const ACTION_FILE_CREATE = 'file.create'; + public const ACTION_FILE_UPDATE = 'file.update'; + public const ACTION_FILE_DELETE = 'file.delete'; + public const ACTION_FILE_ARCHIVE = 'file.archive'; + public const ACTION_FILE_SFTP = 'file.sftp'; + + public const ACTION_STARTUP_READ = 'startup.read'; + public const ACTION_STARTUP_UPDATE = 'startup.update'; + public const ACTION_STARTUP_DOCKER_IMAGE = 'startup.docker-image'; + + public const ACTION_SETTINGS_RENAME = 'settings.rename'; + public const ACTION_SETTINGS_REINSTALL = 'settings.reinstall'; + + public const ACTION_ACTIVITY_READ = 'activity.read'; /** * Should timestamps be used on this model. - * - * @var bool */ public $timestamps = false; /** * The table associated with the model. - * - * @var string */ protected $table = 'permissions'; /** * Fields that are not mass assignable. - * - * @var array */ protected $guarded = ['id', 'created_at', 'updated_at']; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'subuser_id' => 'integer', ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'subuser_id' => 'required', - 'permission' => 'required', + public static array $validationRules = [ + 'subuser_id' => 'required|numeric|min:1', + 'permission' => 'required|string', ]; /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'subuser_id' => 'numeric|min:1', - 'permission' => 'string', - ]; - - /** - * A list of all permissions available for a user. + * All the permissions available on the system. You should use self::permissions() + * to retrieve them, and not directly access this array as it is subject to change. * - * @var array + * @see \Pterodactyl\Models\Permission::permissions() */ - protected static $permissions = [ - 'power' => [ - 'power-start' => 's:power:start', - 'power-stop' => 's:power:stop', - 'power-restart' => 's:power:restart', - 'power-kill' => 's:power:kill', - 'send-command' => 's:command', + protected static array $permissions = [ + 'websocket' => [ + 'description' => 'Allows the user to connect to the server websocket, giving them access to view console output and realtime server stats.', + 'keys' => [ + 'connect' => 'Allows a user to connect to the websocket instance for a server to stream the console.', + ], ], - 'subuser' => [ - 'list-subusers' => null, - 'view-subuser' => null, - 'edit-subuser' => null, - 'create-subuser' => null, - 'delete-subuser' => null, + + 'control' => [ + 'description' => 'Permissions that control a user\'s ability to control the power state of a server, or send commands.', + 'keys' => [ + 'console' => 'Allows a user to send commands to the server instance via the console.', + 'start' => 'Allows a user to start the server if it is stopped.', + 'stop' => 'Allows a user to stop a server if it is running.', + 'restart' => 'Allows a user to perform a server restart. This allows them to start the server if it is offline, but not put the server in a completely stopped state.', + ], ], - 'server' => [ - 'view-allocations' => null, - 'edit-allocation' => null, - 'view-startup' => null, - 'edit-startup' => null, - ], - 'database' => [ - 'view-databases' => null, - 'reset-db-password' => null, - 'delete-database' => null, - 'create-database' => null, + + 'user' => [ + 'description' => 'Permissions that allow a user to manage other subusers on a server. They will never be able to edit their own account, or assign permissions they do not have themselves.', + 'keys' => [ + 'create' => 'Allows a user to create new subusers for the server.', + 'read' => 'Allows the user to view subusers and their permissions for the server.', + 'update' => 'Allows a user to modify other subusers.', + 'delete' => 'Allows a user to delete a subuser from the server.', + ], ], + 'file' => [ - 'access-sftp' => null, - 'list-files' => 's:files:get', - 'edit-files' => 's:files:read', - 'save-files' => 's:files:post', - 'move-files' => 's:files:move', - 'copy-files' => 's:files:copy', - 'compress-files' => 's:files:compress', - 'decompress-files' => 's:files:decompress', - 'create-files' => 's:files:create', - 'upload-files' => 's:files:upload', - 'delete-files' => 's:files:delete', - 'download-files' => 's:files:download', + 'description' => 'Permissions that control a user\'s ability to modify the filesystem for this server.', + 'keys' => [ + 'create' => 'Allows a user to create additional files and folders via the Panel or direct upload.', + 'read' => 'Allows a user to view the contents of a directory, but not view the contents of or download files.', + 'read-content' => 'Allows a user to view the contents of a given file. This will also allow the user to download files.', + 'update' => 'Allows a user to update the contents of an existing file or directory.', + 'delete' => 'Allows a user to delete files or directories.', + 'archive' => 'Allows a user to archive the contents of a directory as well as decompress existing archives on the system.', + 'sftp' => 'Allows a user to connect to SFTP and manage server files using the other assigned file permissions.', + ], ], - 'task' => [ - 'list-schedules' => null, - 'view-schedule' => null, - 'toggle-schedule' => null, - 'queue-schedule' => null, - 'edit-schedule' => null, - 'create-schedule' => null, - 'delete-schedule' => null, + + 'backup' => [ + 'description' => 'Permissions that control a user\'s ability to generate and manage server backups.', + 'keys' => [ + 'create' => 'Allows a user to create new backups for this server.', + 'read' => 'Allows a user to view all backups that exist for this server.', + 'delete' => 'Allows a user to remove backups from the system.', + 'download' => 'Allows a user to download a backup for the server. Danger: this allows a user to access all files for the server in the backup.', + 'restore' => 'Allows a user to restore a backup for the server. Danger: this allows the user to delete all of the server files in the process.', + ], + ], + + // Controls permissions for editing or viewing a server's allocations. + 'allocation' => [ + 'description' => 'Permissions that control a user\'s ability to modify the port allocations for this server.', + 'keys' => [ + 'read' => 'Allows a user to view all allocations currently assigned to this server. Users with any level of access to this server can always view the primary allocation.', + 'create' => 'Allows a user to assign additional allocations to the server.', + 'update' => 'Allows a user to change the primary server allocation and attach notes to each allocation.', + 'delete' => 'Allows a user to delete an allocation from the server.', + ], + ], + + // Controls permissions for editing or viewing a server's startup parameters. + 'startup' => [ + 'description' => 'Permissions that control a user\'s ability to view this server\'s startup parameters.', + 'keys' => [ + 'read' => 'Allows a user to view the startup variables for a server.', + 'update' => 'Allows a user to modify the startup variables for the server.', + 'docker-image' => 'Allows a user to modify the Docker image used when running the server.', + ], + ], + + 'database' => [ + 'description' => 'Permissions that control a user\'s access to the database management for this server.', + 'keys' => [ + 'create' => 'Allows a user to create a new database for this server.', + 'read' => 'Allows a user to view the database associated with this server.', + 'update' => 'Allows a user to rotate the password on a database instance. If the user does not have the view_password permission they will not see the updated password.', + 'delete' => 'Allows a user to remove a database instance from this server.', + 'view_password' => 'Allows a user to view the password associated with a database instance for this server.', + ], + ], + + 'schedule' => [ + 'description' => 'Permissions that control a user\'s access to the schedule management for this server.', + 'keys' => [ + 'create' => 'Allows a user to create new schedules for this server.', // task.create-schedule + 'read' => 'Allows a user to view schedules and the tasks associated with them for this server.', // task.view-schedule, task.list-schedules + 'update' => 'Allows a user to update schedules and schedule tasks for this server.', // task.edit-schedule, task.queue-schedule, task.toggle-schedule + 'delete' => 'Allows a user to delete schedules for this server.', // task.delete-schedule + ], + ], + + 'settings' => [ + 'description' => 'Permissions that control a user\'s access to the settings for this server.', + 'keys' => [ + 'rename' => 'Allows a user to rename this server and change the description of it.', + 'reinstall' => 'Allows a user to trigger a reinstall of this server.', + ], + ], + + 'activity' => [ + 'description' => 'Permissions that control a user\'s access to the server activity logs.', + 'keys' => [ + 'read' => 'Allows a user to view the activity logs for the server.', + ], ], ]; /** - * Return a collection of permissions available. - * - * @param bool $array - * @return array|\Illuminate\Support\Collection + * Returns all the permissions available on the system for a user to + * have when controlling a server. */ - public static function getPermissions($array = false) + public static function permissions(): Collection { - if ($array) { - return collect(self::$permissions)->mapWithKeys(function ($item) { - return $item; - })->all(); - } - - return collect(self::$permissions); - } - - /** - * Find permission by permission node. - * - * @param \Illuminate\Database\Query\Builder $query - * @param string $permission - * @return \Illuminate\Database\Query\Builder - */ - public function scopePermission($query, $permission) - { - return $query->where('permission', $permission); - } - - /** - * Filter permission by server. - * - * @param \Illuminate\Database\Query\Builder $query - * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Database\Query\Builder - */ - public function scopeServer($query, Server $server) - { - return $query->where('server_id', $server->id); + return Collection::make(self::$permissions); } } diff --git a/app/Models/RecoveryToken.php b/app/Models/RecoveryToken.php new file mode 100644 index 000000000..d4ca764a0 --- /dev/null +++ b/app/Models/RecoveryToken.php @@ -0,0 +1,33 @@ + 'required|string', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php index 83971d797..a61bd3cd8 100644 --- a/app/Models/Schedule.php +++ b/app/Models/Schedule.php @@ -2,135 +2,148 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Cron\CronExpression; +use Carbon\CarbonImmutable; +use Illuminate\Container\Container; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Pterodactyl\Contracts\Extensions\HashidsInterface; -class Schedule extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property int $server_id + * @property string $name + * @property string $cron_day_of_week + * @property string $cron_month + * @property string $cron_day_of_month + * @property string $cron_hour + * @property string $cron_minute + * @property bool $is_active + * @property bool $is_processing + * @property bool $only_when_online + * @property \Carbon\Carbon|null $last_run_at + * @property \Carbon\Carbon|null $next_run_at + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $hashid + * @property \Pterodactyl\Models\Server $server + * @property \Pterodactyl\Models\Task[]|\Illuminate\Support\Collection $tasks + */ +class Schedule extends Model { - use Eloquence, Validable; - /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'server_schedule'; + public const RESOURCE_NAME = 'server_schedule'; /** * The table associated with the model. - * - * @var string */ protected $table = 'schedules'; + /** + * Always return the tasks associated with this schedule. + */ + protected $with = ['tasks']; + /** * Mass assignable attributes on this model. - * - * @var array */ protected $fillable = [ 'server_id', 'name', 'cron_day_of_week', + 'cron_month', 'cron_day_of_month', 'cron_hour', 'cron_minute', 'is_active', 'is_processing', + 'only_when_online', 'last_run_at', 'next_run_at', ]; - /** - * @var array - */ protected $casts = [ 'id' => 'integer', 'server_id' => 'integer', 'is_active' => 'boolean', 'is_processing' => 'boolean', + 'only_when_online' => 'boolean', + 'last_run_at' => 'datetime', + 'next_run_at' => 'datetime', ]; - /** - * Columns to mutate to a date. - * - * @var array - */ - protected $dates = [ - self::CREATED_AT, - self::UPDATED_AT, - 'last_run_at', - 'next_run_at', - ]; - - /** - * @var array - */ protected $attributes = [ 'name' => null, 'cron_day_of_week' => '*', + 'cron_month' => '*', 'cron_day_of_month' => '*', 'cron_hour' => '*', 'cron_minute' => '*', 'is_active' => true, 'is_processing' => false, + 'only_when_online' => false, ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'server_id' => 'required', - 'cron_day_of_week' => 'required', - 'cron_day_of_month' => 'required', - 'cron_hour' => 'required', - 'cron_minute' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'server_id' => 'exists:servers,id', - 'name' => 'nullable|string|max:255', - 'cron_day_of_week' => 'string', - 'cron_day_of_month' => 'string', - 'cron_hour' => 'string', - 'cron_minute' => 'string', + public static array $validationRules = [ + 'server_id' => 'required|exists:servers,id', + 'name' => 'required|string|max:191', + 'cron_day_of_week' => 'required|string', + 'cron_month' => 'required|string', + 'cron_day_of_month' => 'required|string', + 'cron_hour' => 'required|string', + 'cron_minute' => 'required|string', 'is_active' => 'boolean', 'is_processing' => 'boolean', + 'only_when_online' => 'boolean', 'last_run_at' => 'nullable|date', 'next_run_at' => 'nullable|date', ]; /** - * Return a hashid encoded string to represent the ID of the schedule. - * - * @return string + * {@inheritDoc} */ - public function getHashidAttribute() + public function getRouteKeyName(): string { - return app()->make('hashids')->encode($this->id); + return $this->getKeyName(); + } + + /** + * Returns the schedule's execution crontab entry as a string. + * + * @throws \Exception + */ + public function getNextRunDate(): CarbonImmutable + { + $formatted = sprintf('%s %s %s %s %s', $this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_month, $this->cron_day_of_week); + + return CarbonImmutable::createFromTimestamp( + (new CronExpression($formatted))->getNextRunDate()->getTimestamp() + ); + } + + /** + * Return a hashid encoded string to represent the ID of the schedule. + */ + public function getHashidAttribute(): string + { + return Container::getInstance()->make(HashidsInterface::class)->encode($this->id); } /** * Return tasks belonging to a schedule. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function tasks() + public function tasks(): HasMany { return $this->hasMany(Task::class); } /** * Return the server model that a schedule belongs to. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function server() + public function server(): BelongsTo { return $this->belongsTo(Server::class); } diff --git a/app/Models/Server.php b/app/Models/Server.php index fd5d9d121..64d1b1440 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -2,265 +2,305 @@ namespace Pterodactyl\Models; -use Schema; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; +use Illuminate\Database\Query\JoinClause; use Znck\Eloquent\Traits\BelongsToThrough; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphToMany; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; +use Pterodactyl\Exceptions\Http\Server\ServerStateConflictException; -class Server extends Model implements CleansAttributes, ValidableContract +/** + * \Pterodactyl\Models\Server. + * + * @property int $id + * @property string|null $external_id + * @property string $uuid + * @property string $uuidShort + * @property int $node_id + * @property string $name + * @property string $description + * @property string|null $status + * @property bool $skip_scripts + * @property int $owner_id + * @property int $memory + * @property int $swap + * @property int $disk + * @property int $io + * @property int $cpu + * @property string|null $threads + * @property bool $oom_killer + * @property int $allocation_id + * @property int $nest_id + * @property int $egg_id + * @property string|null $startup + * @property string $image + * @property int|null $allocation_limit + * @property int|null $database_limit + * @property int $backup_limit + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $installed_at + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ActivityLog[] $activity + * @property int|null $activity_count + * @property \Pterodactyl\Models\Allocation|null $allocation + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Allocation[] $allocations + * @property int|null $allocations_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Backup[] $backups + * @property int|null $backups_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Database[] $databases + * @property int|null $databases_count + * @property \Pterodactyl\Models\Egg|null $egg + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Mount[] $mounts + * @property int|null $mounts_count + * @property \Pterodactyl\Models\Location $location + * @property \Pterodactyl\Models\Nest $nest + * @property \Pterodactyl\Models\Node $node + * @property \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications + * @property int|null $notifications_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Schedule[] $schedules + * @property int|null $schedules_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Subuser[] $subusers + * @property int|null $subusers_count + * @property \Pterodactyl\Models\ServerTransfer|null $transfer + * @property \Pterodactyl\Models\User $user + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\EggVariable[] $variables + * @property int|null $variables_count + * + * @method static \Database\Factories\ServerFactory factory(...$parameters) + * @method static \Illuminate\Database\Eloquent\Builder|Server newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Server newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Server query() + * @method static \Illuminate\Database\Eloquent\Builder|Server whereAllocationId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereAllocationLimit($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereBackupLimit($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereCpu($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereDatabaseLimit($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereDescription($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereDisk($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereEggId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereExternalId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereImage($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereIo($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereMemory($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereNestId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereNodeId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereOomKiller($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereOwnerId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereSkipScripts($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereStartup($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereSwap($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereThreads($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereUuid($value) + * @method static \Illuminate\Database\Eloquent\Builder|Server whereUuidShort($value) + * + * @mixin \Eloquent + */ +class Server extends Model { - use BelongsToThrough, Eloquence, Notifiable, Validable; + use BelongsToThrough; + use Notifiable; /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'server'; + public const RESOURCE_NAME = 'server'; + + public const STATUS_INSTALLING = 'installing'; + public const STATUS_INSTALL_FAILED = 'install_failed'; + public const STATUS_REINSTALL_FAILED = 'reinstall_failed'; + public const STATUS_SUSPENDED = 'suspended'; + public const STATUS_RESTORING_BACKUP = 'restoring_backup'; /** * The table associated with the model. - * - * @var string */ protected $table = 'servers'; /** * Default values when creating the model. We want to switch to disabling OOM killer * on server instances unless the user specifies otherwise in the request. - * - * @var array */ protected $attributes = [ - 'oom_disabled' => true, + 'status' => self::STATUS_INSTALLING, + 'oom_killer' => false, + 'installed_at' => null, ]; /** - * The attributes that should be mutated to dates. - * - * @var array + * The default relationships to load for all server models. */ - protected $dates = [self::CREATED_AT, self::UPDATED_AT, 'deleted_at']; + protected $with = ['allocation']; /** * Fields that are not mass assignable. - * - * @var array */ - protected $guarded = ['id', 'installed', self::CREATED_AT, self::UPDATED_AT, 'deleted_at']; + protected $guarded = ['id', self::CREATED_AT, self::UPDATED_AT, 'deleted_at', 'installed_at']; - /** - * @var array - */ - protected static $applicationRules = [ - 'external_id' => 'sometimes', - 'owner_id' => 'required', - 'name' => 'required', - 'memory' => 'required', - 'swap' => 'required', - 'io' => 'required', - 'cpu' => 'required', - 'oom_disabled' => 'sometimes', - 'disk' => 'required', - 'nest_id' => 'required', - 'egg_id' => 'required', - 'node_id' => 'required', - 'allocation_id' => 'required', - 'pack_id' => 'sometimes', - 'skip_scripts' => 'sometimes', - 'image' => 'required', - 'startup' => 'required', - 'database_limit' => 'present', - 'allocation_limit' => 'sometimes', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'external_id' => 'nullable|string|between:1,191|unique:servers', - 'owner_id' => 'integer|exists:users,id', - 'name' => 'string|min:1|max:255', - 'node_id' => 'exists:nodes,id', + public static array $validationRules = [ + 'external_id' => 'sometimes|nullable|string|between:1,191|unique:servers', + 'owner_id' => 'required|integer|exists:users,id', + 'name' => 'required|string|min:1|max:191', + 'node_id' => 'required|exists:nodes,id', 'description' => 'string', - 'memory' => 'numeric|min:0', - 'swap' => 'numeric|min:-1', - 'io' => 'numeric|between:10,1000', - 'cpu' => 'numeric|min:0', - 'oom_disabled' => 'boolean', - 'disk' => 'numeric|min:0', - 'allocation_id' => 'bail|unique:servers|exists:allocations,id', - 'nest_id' => 'exists:nests,id', - 'egg_id' => 'exists:eggs,id', - 'pack_id' => 'nullable|numeric|min:0', - 'startup' => 'string', - 'skip_scripts' => 'boolean', - 'image' => 'string|max:255', - 'installed' => 'in:0,1,2', - 'database_limit' => 'nullable|integer|min:0', - 'allocation_limit' => 'nullable|integer|min:0', + 'status' => 'nullable|string', + 'memory' => 'required|numeric|min:0', + 'swap' => 'required|numeric|min:-1', + 'io' => 'required|numeric|between:10,1000', + 'cpu' => 'required|numeric|min:0', + 'threads' => 'nullable|regex:/^[0-9-,]+$/', + 'oom_killer' => 'sometimes|boolean', + 'disk' => 'required|numeric|min:0', + 'allocation_id' => 'required|bail|unique:servers|exists:allocations,id', + 'nest_id' => 'required|exists:nests,id', + 'egg_id' => 'required|exists:eggs,id', + 'startup' => 'nullable|string', + 'skip_scripts' => 'sometimes|boolean', + 'image' => 'required|string|max:191', + 'database_limit' => 'present|nullable|integer|min:0', + 'allocation_limit' => 'sometimes|nullable|integer|min:0', + 'backup_limit' => 'present|nullable|integer|min:0', ]; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'node_id' => 'integer', 'skip_scripts' => 'boolean', - 'suspended' => 'integer', 'owner_id' => 'integer', 'memory' => 'integer', 'swap' => 'integer', 'disk' => 'integer', 'io' => 'integer', 'cpu' => 'integer', - 'oom_disabled' => 'boolean', + 'oom_killer' => 'boolean', 'allocation_id' => 'integer', 'nest_id' => 'integer', 'egg_id' => 'integer', - 'pack_id' => 'integer', - 'installed' => 'integer', 'database_limit' => 'integer', 'allocation_limit' => 'integer', + 'backup_limit' => 'integer', + self::CREATED_AT => 'datetime', + self::UPDATED_AT => 'datetime', + 'deleted_at' => 'datetime', + 'installed_at' => 'datetime', ]; /** - * Parameters for search querying. - * - * @var array + * Returns the format for server allocations when communicating with the Daemon. */ - protected $searchableColumns = [ - 'name' => 100, - 'uuid' => 80, - 'uuidShort' => 80, - 'external_id' => 50, - 'user.email' => 40, - 'user.username' => 30, - 'node.name' => 10, - 'pack.name' => 10, - ]; - - /** - * Return the columns available for this table. - * - * @return array - */ - public function getTableColumns() + public function getAllocationMappings(): array { - return Schema::getColumnListing($this->getTable()); + return $this->allocations->where('node_id', $this->node_id)->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(); + } + + public function isInstalled(): bool + { + return $this->status !== self::STATUS_INSTALLING && $this->status !== self::STATUS_INSTALL_FAILED; + } + + public function isSuspended(): bool + { + return $this->status === self::STATUS_SUSPENDED; } /** * Gets the user who owns the server. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class, 'owner_id'); } /** * Gets the subusers associated with a server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function subusers() + public function subusers(): HasMany { return $this->hasMany(Subuser::class, 'server_id', 'id'); } /** * Gets the default allocation for a server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasOne */ - public function allocation() + public function allocation(): HasOne { return $this->hasOne(Allocation::class, 'id', 'allocation_id'); } /** * Gets all allocations associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function allocations() + public function allocations(): HasMany { return $this->hasMany(Allocation::class, 'server_id'); } - /** - * Gets information for the pack associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function pack() - { - return $this->belongsTo(Pack::class); - } - /** * Gets information for the nest associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function nest() + public function nest(): BelongsTo { return $this->belongsTo(Nest::class); } /** * Gets information for the egg associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function egg() + public function egg(): HasOne { - return $this->belongsTo(Egg::class); + return $this->hasOne(Egg::class, 'id', 'egg_id'); } /** * Gets information for the service variables associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function variables() + public function variables(): HasMany { - return $this->hasMany(ServerVariable::class); + return $this->hasMany(EggVariable::class, 'egg_id', 'egg_id') + ->select(['egg_variables.*', 'server_variables.variable_value as server_value']) + ->leftJoin('server_variables', function (JoinClause $join) { + // Don't forget to join against the server ID as well since the way we're using this relationship + // would actually return all the variables and their values for _all_ servers using that egg, + // rather than only the server for this model. + // + // @see https://github.com/pterodactyl/panel/issues/2250 + $join->on('server_variables.variable_id', 'egg_variables.id') + ->where('server_variables.server_id', $this->id); + }); } /** * Gets information for the node associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function node() + public function node(): BelongsTo { return $this->belongsTo(Node::class); } /** * Gets information for the tasks associated with this server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function schedule() + public function schedules(): HasMany { return $this->hasMany(Schedule::class); } /** * Gets all databases associated with a server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function databases() + public function databases(): HasMany { return $this->hasMany(Database::class); } @@ -268,32 +308,76 @@ class Server extends Model implements CleansAttributes, ValidableContract /** * Returns the location that a server belongs to. * - * @return \Znck\Eloquent\Relations\BelongsToThrough - * * @throws \Exception */ - public function location() + public function location(): \Znck\Eloquent\Relations\BelongsToThrough { return $this->belongsToThrough(Location::class, Node::class); } /** - * Return the key belonging to the server owner. - * - * @return \Illuminate\Database\Eloquent\Relations\HasOne + * Returns the associated server transfer. */ - public function key() + public function transfer(): HasOne { - return $this->hasOne(DaemonKey::class, 'user_id', 'owner_id'); + return $this->hasOne(ServerTransfer::class)->whereNull('successful')->orderByDesc('id'); + } + + public function backups(): HasMany + { + return $this->hasMany(Backup::class); } /** - * Returns all of the daemon keys belonging to this server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * Returns all mounts that have this server has mounted. */ - public function keys() + public function mounts(): HasManyThrough { - return $this->hasMany(DaemonKey::class); + return $this->hasManyThrough(Mount::class, MountServer::class, 'server_id', 'id', 'id', 'mount_id'); + } + + /** + * Returns all of the activity log entries where the server is the subject. + */ + public function activity(): MorphToMany + { + return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects'); + } + + /** + * Checks if the server is currently in a user-accessible state. If not, an + * exception is raised. This should be called whenever something needs to make + * sure the server is not in a weird state that should block user access. + * + * @throws \Pterodactyl\Exceptions\Http\Server\ServerStateConflictException + */ + public function validateCurrentState() + { + if ( + $this->isSuspended() || + $this->node->isUnderMaintenance() || + !$this->isInstalled() || + $this->status === self::STATUS_RESTORING_BACKUP || + !is_null($this->transfer) + ) { + throw new ServerStateConflictException($this); + } + } + + /** + * Checks if the server is currently in a transferable state. If not, an + * exception is raised. This should be called whenever something needs to make + * sure the server is able to be transferred and is not currently being transferred + * or installed. + */ + public function validateTransferState() + { + if ( + !$this->isInstalled() || + $this->status === self::STATUS_RESTORING_BACKUP || + !is_null($this->transfer) + ) { + throw new ServerStateConflictException($this); + } } } diff --git a/app/Models/ServerTransfer.php b/app/Models/ServerTransfer.php new file mode 100644 index 000000000..da4618828 --- /dev/null +++ b/app/Models/ServerTransfer.php @@ -0,0 +1,94 @@ + 'int', + 'old_node' => 'int', + 'new_node' => 'int', + 'old_allocation' => 'int', + 'new_allocation' => 'int', + 'old_additional_allocations' => 'array', + 'new_additional_allocations' => 'array', + 'successful' => 'bool', + 'archived' => 'bool', + ]; + + public static array $validationRules = [ + 'server_id' => 'required|numeric|exists:servers,id', + 'old_node' => 'required|numeric', + 'new_node' => 'required|numeric', + 'old_allocation' => 'required|numeric', + 'new_allocation' => 'required|numeric', + 'old_additional_allocations' => 'nullable|array', + 'old_additional_allocations.*' => 'numeric', + 'new_additional_allocations' => 'nullable|array', + 'new_additional_allocations.*' => 'numeric', + 'successful' => 'sometimes|nullable|boolean', + ]; + + /** + * Gets the server associated with a server transfer. + */ + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + /** + * Gets the source node associated with a server transfer. + */ + public function oldNode(): HasOne + { + return $this->hasOne(Node::class, 'id', 'old_node'); + } + + /** + * Gets the target node associated with a server transfer. + */ + public function newNode(): HasOne + { + return $this->hasOne(Node::class, 'id', 'new_node'); + } +} diff --git a/app/Models/ServerVariable.php b/app/Models/ServerVariable.php index f706d5942..46ae57dd4 100644 --- a/app/Models/ServerVariable.php +++ b/app/Models/ServerVariable.php @@ -2,76 +2,55 @@ namespace Pterodactyl\Models; -use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * @property int $id + * @property int $server_id + * @property int $variable_id + * @property string $variable_value + * @property \Carbon\CarbonImmutable|null $created_at + * @property \Carbon\CarbonImmutable|null $updated_at + * @property \Pterodactyl\Models\EggVariable $variable + * @property \Pterodactyl\Models\Server $server + */ class ServerVariable extends Model { /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'server_variable'; + public const RESOURCE_NAME = 'server_variable'; + + protected bool $immutableDates = true; - /** - * The table associated with the model. - * - * @var string - */ protected $table = 'server_variables'; - /** - * Fields that are not mass assignable. - * - * @var array - */ protected $guarded = ['id', 'created_at', 'updated_at']; - /** - * Cast values to correct type. - * - * @var array - */ protected $casts = [ - 'server_id' => 'integer', - 'variable_id' => 'integer', - ]; + 'server_id' => 'integer', + 'variable_id' => 'integer', + ]; + + public static array $validationRules = [ + 'server_id' => 'required|int', + 'variable_id' => 'required|int', + 'variable_value' => 'string', + ]; /** - * Determine if variable is viewable by users. - * - * @return bool + * Returns the server this variable is associated with. */ - public function getUserCanViewAttribute() + public function server(): BelongsTo { - return (bool) $this->variable->user_viewable; - } - - /** - * Determine if variable is editable by users. - * - * @return bool - */ - public function getUserCanEditAttribute() - { - return (bool) $this->variable->user_editable; - } - - /** - * Determine if variable is required. - * - * @return bool - */ - public function getRequiredAttribute() - { - return $this->variable->required; + return $this->belongsTo(Server::class); } /** * Returns information about a given variables parent. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function variable() + public function variable(): BelongsTo { return $this->belongsTo(EggVariable::class, 'variable_id'); } diff --git a/app/Models/Session.php b/app/Models/Session.php index 3eb9d526e..baacdb875 100644 --- a/app/Models/Session.php +++ b/app/Models/Session.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Models; @@ -15,15 +8,11 @@ class Session extends Model { /** * The table associated with the model. - * - * @var string */ protected $table = 'sessions'; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'id' => 'string', diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 90d41f3d4..b8812bc66 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -2,38 +2,26 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; - -class Setting extends Model implements CleansAttributes, ValidableContract +/** + * Pterodactyl\Models\Setting. + * + * @property int $id + * @property string $key + * @property string $value + */ +class Setting extends Model { - use Eloquence, Validable; - /** * The table associated with the model. - * - * @var string */ protected $table = 'settings'; - /** - * @var bool - */ public $timestamps = false; - /** - * @var array - */ protected $fillable = ['key', 'value']; - /** - * @var array - */ - protected static $applicationRules = [ - 'key' => 'required|string|between:1,255', + public static array $validationRules = [ + 'key' => 'required|string|between:1,191', 'value' => 'string', ]; } diff --git a/app/Models/Subuser.php b/app/Models/Subuser.php index 93c62217a..40950d5b2 100644 --- a/app/Models/Subuser.php +++ b/app/Models/Subuser.php @@ -2,110 +2,85 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; -class Subuser extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property int $user_id + * @property int $server_id + * @property array $permissions + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Pterodactyl\Models\User $user + * @property \Pterodactyl\Models\Server $server + */ +class Subuser extends Model { - use Eloquence, Notifiable, Validable; + use Notifiable; /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'server_subuser'; + public const RESOURCE_NAME = 'server_subuser'; /** * The table associated with the model. - * - * @var string */ protected $table = 'subusers'; /** * Fields that are not mass assignable. - * - * @var array */ protected $guarded = ['id', 'created_at', 'updated_at']; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ - 'user_id' => 'integer', - 'server_id' => 'integer', + 'user_id' => 'int', + 'server_id' => 'int', + 'permissions' => 'array', ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'user_id' => 'required', - 'server_id' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'user_id' => 'numeric|exists:users,id', - 'server_id' => 'numeric|exists:servers,id', + public static array $validationRules = [ + 'user_id' => 'required|numeric|exists:users,id', + 'server_id' => 'required|numeric|exists:servers,id', + 'permissions' => 'nullable|array', + 'permissions.*' => 'string', ]; /** * Return a hashid encoded string to represent the ID of the subuser. - * - * @return string */ - public function getHashidAttribute() + public function getHashidAttribute(): string { return app()->make('hashids')->encode($this->id); } /** * Gets the server associated with a subuser. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function server() + public function server(): BelongsTo { return $this->belongsTo(Server::class); } /** * Gets the user associated with a subuser. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Gets the permissions associated with a subuser. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function permissions() + public function permissions(): HasMany { return $this->hasMany(Permission::class); } - - /** - * Return the key that belongs to this subuser for the server. - * - * @return \Illuminate\Database\Eloquent\Relations\HasOne - */ - public function key() - { - return $this->hasOne(DaemonKey::class, 'server_id', 'server_id')->where('daemon_keys.user_id', '=', $this->user_id); - } } diff --git a/app/Models/Task.php b/app/Models/Task.php index 28c8e3237..8e772731e 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -2,41 +2,55 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; -use Illuminate\Database\Eloquent\Model; +use Illuminate\Container\Container; use Znck\Eloquent\Traits\BelongsToThrough; -use Sofa\Eloquence\Contracts\CleansAttributes; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Pterodactyl\Contracts\Extensions\HashidsInterface; -class Task extends Model implements CleansAttributes, ValidableContract +/** + * @property int $id + * @property int $schedule_id + * @property int $sequence_id + * @property string $action + * @property string $payload + * @property int $time_offset + * @property bool $is_queued + * @property bool $continue_on_failure + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $hashid + * @property \Pterodactyl\Models\Schedule $schedule + * @property \Pterodactyl\Models\Server $server + */ +class Task extends Model { - use BelongsToThrough, Eloquence, Validable; + use BelongsToThrough; /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'schedule_task'; + public const RESOURCE_NAME = 'schedule_task'; + + /** + * The default actions that can exist for a task in Pterodactyl. + */ + public const ACTION_POWER = 'power'; + public const ACTION_COMMAND = 'command'; + public const ACTION_BACKUP = 'backup'; /** * The table associated with the model. - * - * @var string */ protected $table = 'tasks'; /** * Relationships to be updated when this model is updated. - * - * @var array */ protected $touches = ['schedule']; /** * Fields that are mass assignable. - * - * @var array */ protected $fillable = [ 'schedule_id', @@ -45,12 +59,11 @@ class Task extends Model implements CleansAttributes, ValidableContract 'payload', 'time_offset', 'is_queued', + 'continue_on_failure', ]; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'id' => 'integer', @@ -58,67 +71,56 @@ class Task extends Model implements CleansAttributes, ValidableContract 'sequence_id' => 'integer', 'time_offset' => 'integer', 'is_queued' => 'boolean', + 'continue_on_failure' => 'boolean', ]; /** * Default attributes when creating a new model. - * - * @var array */ protected $attributes = [ + 'time_offset' => 0, 'is_queued' => false, + 'continue_on_failure' => false, ]; - /** - * @var array - */ - protected static $applicationRules = [ - 'schedule_id' => 'required', - 'sequence_id' => 'required', - 'action' => 'required', - 'payload' => 'required', - 'time_offset' => 'required', - ]; - - /** - * @var array - */ - protected static $dataIntegrityRules = [ - 'schedule_id' => 'numeric|exists:schedules,id', - 'sequence_id' => 'numeric|min:1', - 'action' => 'string', - 'payload' => 'string', - 'time_offset' => 'numeric|between:0,900', + public static array $validationRules = [ + 'schedule_id' => 'required|numeric|exists:schedules,id', + 'sequence_id' => 'required|numeric|min:1', + 'action' => 'required|string', + 'payload' => 'required_unless:action,backup|string', + 'time_offset' => 'required|numeric|between:0,900', 'is_queued' => 'boolean', + 'continue_on_failure' => 'boolean', ]; + /** + * {@inheritDoc} + */ + public function getRouteKeyName(): string + { + return $this->getKeyName(); + } + /** * Return a hashid encoded string to represent the ID of the task. - * - * @return string */ - public function getHashidAttribute() + public function getHashidAttribute(): string { - return app()->make('hashids')->encode($this->id); + return Container::getInstance()->make(HashidsInterface::class)->encode($this->id); } /** * Return the schedule that a task belongs to. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function schedule() + public function schedule(): BelongsTo { return $this->belongsTo(Schedule::class); } /** * Return the server a task is assigned to, acts as a belongsToThrough. - * - * @return \Znck\Eloquent\Relations\BelongsToThrough - * @throws \Exception */ - public function server() + public function server(): \Znck\Eloquent\Relations\BelongsToThrough { return $this->belongsToThrough(Server::class, Schedule::class); } diff --git a/app/Models/TaskLog.php b/app/Models/TaskLog.php index ec85677e1..81b7fdb78 100644 --- a/app/Models/TaskLog.php +++ b/app/Models/TaskLog.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Models; @@ -15,33 +8,23 @@ class TaskLog extends Model { /** * The table associated with the model. - * - * @var string */ protected $table = 'tasks_log'; /** * Fields that are not mass assignable. - * - * @var array */ protected $guarded = ['id', 'created_at', 'updated_at']; /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'id' => 'integer', 'task_id' => 'integer', 'run_status' => 'integer', + 'run_time' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; - - /** - * The attributes that should be mutated to dates. - * - * @var array - */ - protected $dates = ['run_time', 'created_at', 'updated_at']; } diff --git a/app/Models/Traits/HasAccessTokens.php b/app/Models/Traits/HasAccessTokens.php new file mode 100644 index 000000000..ed042ccfa --- /dev/null +++ b/app/Models/Traits/HasAccessTokens.php @@ -0,0 +1,41 @@ +hasMany(Sanctum::$personalAccessTokenModel); + } + + public function createToken(?string $memo, ?array $ips): NewAccessToken + { + /** @var \Pterodactyl\Models\ApiKey $token */ + $token = $this->tokens()->forceCreate([ + 'user_id' => $this->id, + 'key_type' => ApiKey::TYPE_ACCOUNT, + 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_ACCOUNT), + 'token' => encrypt($plain = Str::random(ApiKey::KEY_LENGTH)), + 'memo' => $memo ?? '', + 'allowed_ips' => $ips ?? [], + ]); + + return new NewAccessToken($token, $plain); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 6732b23af..3446bf807 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,73 +2,126 @@ namespace Pterodactyl\Models; -use Sofa\Eloquence\Eloquence; -use Sofa\Eloquence\Validable; use Pterodactyl\Rules\Username; +use Pterodactyl\Facades\Activity; +use Illuminate\Support\Collection; use Illuminate\Validation\Rules\In; use Illuminate\Auth\Authenticatable; -use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; -use Sofa\Eloquence\Contracts\CleansAttributes; +use Illuminate\Database\Eloquent\Builder; +use Pterodactyl\Models\Traits\HasAccessTokens; use Illuminate\Auth\Passwords\CanResetPassword; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Relations\HasOne; use Pterodactyl\Traits\Helpers\AvailableLanguages; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\Access\Authorizable; -use Sofa\Eloquence\Contracts\Validable as ValidableContract; +use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Pterodactyl\Notifications\SendPasswordReset as ResetPasswordNotification; +/** + * Pterodactyl\Models\User. + * + * @property int $id + * @property string|null $external_id + * @property string $uuid + * @property string $username + * @property string $email + * @property string $password + * @property string|null $remember_token + * @property string $language + * @property int|null $admin_role_id + * @property bool $root_admin + * @property bool $use_totp + * @property string|null $totp_secret + * @property \Illuminate\Support\Carbon|null $totp_authenticated_at + * @property bool $gravatar + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property string $avatar_url + * @property string|null $admin_role_name + * @property string $md5 + * @property \Pterodactyl\Models\AdminRole|null $adminRole + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ApiKey[] $apiKeys + * @property int|null $api_keys_count + * @property \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications + * @property int|null $notifications_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\RecoveryToken[] $recoveryTokens + * @property int|null $recovery_tokens_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Server[] $servers + * @property int|null $servers_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\UserSSHKey[] $sshKeys + * @property int|null $ssh_keys_count + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ApiKey[] $tokens + * @property int|null $tokens_count + * + * @method static \Database\Factories\UserFactory factory(...$parameters) + * @method static Builder|User newModelQuery() + * @method static Builder|User newQuery() + * @method static Builder|User query() + * @method static Builder|User whereCreatedAt($value) + * @method static Builder|User whereEmail($value) + * @method static Builder|User whereExternalId($value) + * @method static Builder|User whereGravatar($value) + * @method static Builder|User whereId($value) + * @method static Builder|User whereLanguage($value) + * @method static Builder|User whereNameFirst($value) + * @method static Builder|User whereNameLast($value) + * @method static Builder|User wherePassword($value) + * @method static Builder|User whereRememberToken($value) + * @method static Builder|User whereRootAdmin($value) + * @method static Builder|User whereTotpAuthenticatedAt($value) + * @method static Builder|User whereTotpSecret($value) + * @method static Builder|User whereUpdatedAt($value) + * @method static Builder|User whereUseTotp($value) + * @method static Builder|User whereUsername($value) + * @method static Builder|User whereUuid($value) + * + * @mixin \Barryvdh\LaravelIdeHelper\Eloquent + * @mixin \Illuminate\Database\Query\Builder + * @mixin \Illuminate\Database\Eloquent\Builder + */ class User extends Model implements AuthenticatableContract, AuthorizableContract, - CanResetPasswordContract, - CleansAttributes, - ValidableContract + CanResetPasswordContract { - use Authenticatable, Authorizable, AvailableLanguages, CanResetPassword, Eloquence, Notifiable, Validable { - gatherRules as eloquenceGatherRules; - } + use Authenticatable; + use Authorizable; + use AvailableLanguages; + use CanResetPassword; + use HasAccessTokens; + use Notifiable; - const USER_LEVEL_USER = 0; - const USER_LEVEL_ADMIN = 1; - - const FILTER_LEVEL_ALL = 0; - const FILTER_LEVEL_OWNER = 1; - const FILTER_LEVEL_ADMIN = 2; - const FILTER_LEVEL_SUBUSER = 3; + public const USER_LEVEL_USER = 0; + public const USER_LEVEL_ADMIN = 1; /** * The resource name for this model when it is transformed into an * API representation using fractal. */ - const RESOURCE_NAME = 'user'; + public const RESOURCE_NAME = 'user'; /** * Level of servers to display when using access() on a user. - * - * @var string */ - protected $accessLevel = 'all'; + protected string $accessLevel = 'all'; /** * The table associated with the model. - * - * @var string */ protected $table = 'users'; /** * A list of mass-assignable variables. - * - * @var array */ protected $fillable = [ 'external_id', 'username', 'email', - 'name_first', - 'name_last', 'password', 'language', 'use_totp', @@ -80,45 +133,21 @@ class User extends Model implements /** * Cast values to correct type. - * - * @var array */ protected $casts = [ 'root_admin' => 'boolean', 'use_totp' => 'boolean', 'gravatar' => 'boolean', + 'totp_authenticated_at' => 'datetime', ]; - /** - * @var array - */ - protected $dates = [self::CREATED_AT, self::UPDATED_AT, 'totp_authenticated_at']; - /** * The attributes excluded from the model's JSON form. - * - * @var array */ protected $hidden = ['password', 'remember_token', 'totp_secret', 'totp_authenticated_at']; - /** - * Parameters for search querying. - * - * @var array - */ - protected $searchableColumns = [ - 'username' => 100, - 'email' => 100, - 'external_id' => 80, - 'uuid' => 80, - 'name_first' => 40, - 'name_last' => 40, - ]; - /** * Default values for specific fields in the database. - * - * @var array */ protected $attributes = [ 'external_id' => null, @@ -128,36 +157,15 @@ class User extends Model implements 'totp_secret' => null, ]; - /** - * Rules verifying that the data passed in forms is valid and meets application logic rules. - * - * @var array - */ - protected static $applicationRules = [ - 'uuid' => 'required', - 'email' => 'required', - 'external_id' => 'sometimes', - 'username' => 'required', - 'name_first' => 'required', - 'name_last' => 'required', - 'password' => 'sometimes', - 'language' => 'sometimes', - 'use_totp' => 'sometimes', - ]; - /** * Rules verifying that the data being stored matches the expectations of the database. - * - * @var array */ - protected static $dataIntegrityRules = [ - 'uuid' => 'string|size:36|unique:users,uuid', - 'email' => 'email|unique:users,email', - 'external_id' => 'nullable|string|max:255|unique:users,external_id', - 'username' => 'between:1,255|unique:users,username', - 'name_first' => 'string|between:1,255', - 'name_last' => 'string|between:1,255', - 'password' => 'nullable|string', + public static array $validationRules = [ + 'uuid' => 'required|string|size:36|unique:users,uuid', + 'email' => 'required|email|between:1,191|unique:users,email', + 'external_id' => 'sometimes|nullable|string|max:191|unique:users,external_id', + 'username' => 'required|between:1,191|unique:users,username', + 'password' => 'sometimes|nullable|string', 'root_admin' => 'boolean', 'language' => 'string', 'use_totp' => 'boolean', @@ -168,15 +176,26 @@ class User extends Model implements * Implement language verification by overriding Eloquence's gather * rules function. */ - protected static function gatherRules() + public static function getRules(): array { - $rules = self::eloquenceGatherRules(); - $rules['language'][] = new In(array_keys((new self)->getAvailableLanguages())); - $rules['username'][] = new Username; + $rules = parent::getRules(); + + $rules['language'][] = new In(array_keys((new self())->getAvailableLanguages())); + $rules['username'][] = new Username(); return $rules; } + /** + * Return the user model in a format that can be passed over to React templates. + */ + public function toReactObject(): array + { + return Collection::make($this->append(['avatar_url', 'admin_role_name'])->toArray()) + ->except(['id', 'external_id', 'admin_role', 'admin_role_id']) + ->toArray(); + } + /** * Send the password reset notification. * @@ -184,66 +203,90 @@ class User extends Model implements */ public function sendPasswordResetNotification($token) { + Activity::event('auth:reset-password') + ->withRequestMetadata() + ->subject($this) + ->log('sending password reset email'); + $this->notify(new ResetPasswordNotification($token)); } /** * Store the username as a lowercase string. - * - * @param string $value */ public function setUsernameAttribute(string $value) { $this->attributes['username'] = mb_strtolower($value); } - /** - * Return a concatenated result for the accounts full name. - * - * @return string - */ - public function getNameAttribute() + public function avatarUrl(): Attribute { - return $this->name_first . ' ' . $this->name_last; + return Attribute::make( + get: fn () => 'https://www.gravatar.com/avatar/' . $this->md5 . '.jpg', + ); + } + + public function adminRoleName(): Attribute + { + return Attribute::make( + get: fn () => is_null($this->adminRole) ? ($this->root_admin ? 'None' : null) : $this->adminRole->name, + ); + } + + public function md5(): Attribute + { + return Attribute::make( + get: fn () => md5(strtolower($this->email)), + ); } /** - * Returns all permissions that a user has. - * - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough + * Returns all the activity logs where this user is the subject — not to + * be confused by activity logs where this user is the _actor_. */ - public function permissions() + public function activity(): MorphToMany { - return $this->hasManyThrough(Permission::class, Subuser::class); + return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects'); } - /** - * Returns all servers that a user owns. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function servers() + public function adminRole(): HasOne + { + return $this->hasOne(AdminRole::class, 'id', 'admin_role_id'); + } + + public function apiKeys(): HasMany + { + return $this->hasMany(ApiKey::class) + ->where('key_type', ApiKey::TYPE_ACCOUNT); + } + + public function recoveryTokens(): HasMany + { + return $this->hasMany(RecoveryToken::class); + } + + public function servers(): HasMany { return $this->hasMany(Server::class, 'owner_id'); } - /** - * Return all servers that user is listed as a subuser of directly. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function subuserOf() + public function sshKeys(): HasMany { - return $this->hasMany(Subuser::class); + return $this->hasMany(UserSSHKey::class); } /** - * Return all of the daemon keys that a user belongs to. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * Returns all the servers that a user can access by way of being the owner of the + * server, or because they are assigned as a subuser for that server. */ - public function keys() + public function accessibleServers(): Builder { - return $this->hasMany(DaemonKey::class); + return Server::query() + ->select('servers.*') + ->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id') + ->where(function (Builder $builder) { + $builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id); + }) + ->groupBy('servers.id'); } } diff --git a/app/Models/UserSSHKey.php b/app/Models/UserSSHKey.php new file mode 100644 index 000000000..fb3c8f395 --- /dev/null +++ b/app/Models/UserSSHKey.php @@ -0,0 +1,64 @@ + ['required', 'string'], + 'fingerprint' => ['required', 'string'], + 'public_key' => ['required', 'string'], + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Notifications/AccountCreated.php b/app/Notifications/AccountCreated.php index 7dd258dd4..50d7f1f42 100644 --- a/app/Notifications/AccountCreated.php +++ b/app/Notifications/AccountCreated.php @@ -12,59 +12,33 @@ class AccountCreated extends Notification implements ShouldQueue { use Queueable; - /** - * The authentication token to be used for the user to set their - * password for the first time. - * - * @var string|null - */ - public $token; - - /** - * The user model for the created user. - * - * @var \Pterodactyl\Models\User - */ - public $user; - /** * Create a new notification instance. - * - * @param \Pterodactyl\Models\User $user - * @param string|null $token */ - public function __construct(User $user, string $token = null) + public function __construct(public User $user, public ?string $token = null) { - $this->token = $token; - $this->user = $user; } /** * Get the notification's delivery channels. - * - * @param mixed $notifiable - * @return array */ - public function via($notifiable) + public function via(): array { return ['mail']; } /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(): MailMessage { - $message = (new MailMessage) - ->greeting('Hello ' . $this->user->name . '!') + $message = (new MailMessage()) + ->greeting('Hello!') ->line('You are receiving this email because an account has been created for you on ' . config('app.name') . '.') ->line('Username: ' . $this->user->username) ->line('Email: ' . $this->user->email); - if (! is_null($this->token)) { + if (!is_null($this->token)) { return $message->action('Setup Your Account', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($this->user->email))); } diff --git a/app/Notifications/AddedToServer.php b/app/Notifications/AddedToServer.php index 2ecaa45f3..c78c7db8b 100644 --- a/app/Notifications/AddedToServer.php +++ b/app/Notifications/AddedToServer.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Notifications; @@ -18,15 +11,10 @@ class AddedToServer extends Notification implements ShouldQueue { use Queueable; - /** - * @var object - */ - public $server; + public object $server; /** * Create a new notification instance. - * - * @param array $server */ public function __construct(array $server) { @@ -35,27 +23,21 @@ class AddedToServer extends Notification implements ShouldQueue /** * Get the notification's delivery channels. - * - * @param mixed $notifiable - * @return array */ - public function via($notifiable) + public function via(): array { return ['mail']; } /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(): MailMessage { - return (new MailMessage) + return (new MailMessage()) ->greeting('Hello ' . $this->server->user . '!') ->line('You have been added as a subuser for the following server, allowing you certain control over the server.') ->line('Server Name: ' . $this->server->name) - ->action('Visit Server', route('server.index', $this->server->uuidShort)); + ->action('Visit Server', url('/server/' . $this->server->uuidShort)); } } diff --git a/app/Notifications/MailTested.php b/app/Notifications/MailTested.php index fdb95f5cb..3ef25a57b 100644 --- a/app/Notifications/MailTested.php +++ b/app/Notifications/MailTested.php @@ -8,26 +8,20 @@ use Illuminate\Notifications\Messages\MailMessage; class MailTested extends Notification { - /** - * @var \Pterodactyl\Models\User - */ - private $user; - - public function __construct(User $user) + public function __construct(private User $user) { - $this->user = $user; } - public function via() + public function via(): array { return ['mail']; } - public function toMail() + public function toMail(): MailMessage { - return (new MailMessage) + return (new MailMessage()) ->subject('Pterodactyl Test Message') - ->greeting('Hello ' . $this->user->name . '!') + ->greeting('Hello ' . $this->user->username . '!') ->line('This is a test of the Pterodactyl mail system. You\'re good to go!'); } } diff --git a/app/Notifications/RemovedFromServer.php b/app/Notifications/RemovedFromServer.php index d23aae54a..492dc60e6 100644 --- a/app/Notifications/RemovedFromServer.php +++ b/app/Notifications/RemovedFromServer.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Notifications; @@ -18,15 +11,10 @@ class RemovedFromServer extends Notification implements ShouldQueue { use Queueable; - /** - * @var object - */ - public $server; + public object $server; /** * Create a new notification instance. - * - * @param array $server */ public function __construct(array $server) { @@ -35,24 +23,18 @@ class RemovedFromServer extends Notification implements ShouldQueue /** * Get the notification's delivery channels. - * - * @param mixed $notifiable - * @return array */ - public function via($notifiable) + public function via(): array { return ['mail']; } /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(): MailMessage { - return (new MailMessage) + return (new MailMessage()) ->error() ->greeting('Hello ' . $this->server->user . '.') ->line('You have been removed as a subuser for the following server.') diff --git a/app/Notifications/SendPasswordReset.php b/app/Notifications/SendPasswordReset.php index 2f1519cec..b424c8293 100644 --- a/app/Notifications/SendPasswordReset.php +++ b/app/Notifications/SendPasswordReset.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Notifications; @@ -18,46 +11,30 @@ class SendPasswordReset extends Notification implements ShouldQueue { use Queueable; - /** - * The password reset token. - * - * @var string - */ - public $token; - /** * Create a new notification instance. - * - * @param string $token */ - public function __construct($token) + public function __construct(public string $token) { - $this->token = $token; } /** * Get the notification's delivery channels. - * - * @param mixed $notifiable - * @return array */ - public function via($notifiable) + public function via(): array { return ['mail']; } /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(mixed $notifiable): MailMessage { - return (new MailMessage) + return (new MailMessage()) ->subject('Reset Password') ->line('You are receiving this email because we received a password reset request for your account.') - ->action('Reset Password', url('/auth/password/reset/' . $this->token . '?email=' . $notifiable->email)) + ->action('Reset Password', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($notifiable->email))) ->line('If you did not request a password reset, no further action is required.'); } } diff --git a/app/Notifications/ServerInstalled.php b/app/Notifications/ServerInstalled.php index fe9747223..3eb93e79d 100644 --- a/app/Notifications/ServerInstalled.php +++ b/app/Notifications/ServerInstalled.php @@ -2,9 +2,12 @@ namespace Pterodactyl\Notifications; +use Pterodactyl\Models\User; use Illuminate\Bus\Queueable; use Pterodactyl\Events\Event; +use Pterodactyl\Models\Server; use Illuminate\Container\Container; +use Pterodactyl\Events\Server\Installed; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Pterodactyl\Contracts\Core\ReceivesEvents; @@ -15,28 +18,22 @@ class ServerInstalled extends Notification implements ShouldQueue, ReceivesEvent { use Queueable; - /** - * @var \Pterodactyl\Models\Server - */ - public $server; + public Server $server; - /** - * @var \Pterodactyl\Models\User - */ - public $user; + public User $user; /** * Handle a direct call to this notification from the server installed event. This is configured * in the event service provider. - * - * @param \Pterodactyl\Events\Event|\Pterodactyl\Events\Server\Installed $event */ - public function handle(Event $event): void + public function handle(Event|Installed $notification): void { - $event->server->loadMissing('user'); + abort_unless($notification instanceof Installed, 500); + /* @var Installed $notification */ + $notification->server->loadMissing('user'); - $this->server = $event->server; - $this->user = $event->server->user; + $this->server = $notification->server; + $this->user = $notification->server->user; // Since we are calling this notification directly from an event listener we need to fire off the dispatcher // to send the email now. Don't use send() or you'll end up firing off two different events. @@ -45,22 +42,18 @@ class ServerInstalled extends Notification implements ShouldQueue, ReceivesEvent /** * Get the notification's delivery channels. - * - * @return array */ - public function via() + public function via(): array { return ['mail']; } /** * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail() + public function toMail(): MailMessage { - return (new MailMessage) + return (new MailMessage()) ->greeting('Hello ' . $this->user->username . '.') ->line('Your server has finished installing and is now ready for you to use.') ->line('Server Name: ' . $this->server->name) diff --git a/app/Observers/EggVariableObserver.php b/app/Observers/EggVariableObserver.php new file mode 100644 index 000000000..a18718f47 --- /dev/null +++ b/app/Observers/EggVariableObserver.php @@ -0,0 +1,22 @@ +field_type) { + unset($variable->field_type); + } + } + + public function updating(EggVariable $variable): void + { + if ($variable->field_type) { + unset($variable->field_type); + } + } +} diff --git a/app/Observers/ServerObserver.php b/app/Observers/ServerObserver.php index 00db4b334..51276523e 100644 --- a/app/Observers/ServerObserver.php +++ b/app/Observers/ServerObserver.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Observers; @@ -19,80 +12,64 @@ class ServerObserver /** * Listen to the Server creating event. - * - * @param \Pterodactyl\Models\Server $server */ - public function creating(Server $server) + public function creating(Server $server): void { event(new Events\Server\Creating($server)); } /** * Listen to the Server created event. - * - * @param \Pterodactyl\Models\Server $server */ - public function created(Server $server) + public function created(Server $server): void { event(new Events\Server\Created($server)); } /** * Listen to the Server deleting event. - * - * @param \Pterodactyl\Models\Server $server */ - public function deleting(Server $server) + public function deleting(Server $server): void { event(new Events\Server\Deleting($server)); } /** * Listen to the Server deleted event. - * - * @param \Pterodactyl\Models\Server $server */ - public function deleted(Server $server) + public function deleted(Server $server): void { event(new Events\Server\Deleted($server)); } /** * Listen to the Server saving event. - * - * @param \Pterodactyl\Models\Server $server */ - public function saving(Server $server) + public function saving(Server $server): void { event(new Events\Server\Saving($server)); } /** * Listen to the Server saved event. - * - * @param \Pterodactyl\Models\Server $server */ - public function saved(Server $server) + public function saved(Server $server): void { event(new Events\Server\Saved($server)); } /** * Listen to the Server updating event. - * - * @param \Pterodactyl\Models\Server $server */ - public function updating(Server $server) + public function updating(Server $server): void { event(new Events\Server\Updating($server)); } /** * Listen to the Server saved event. - * - * @param \Pterodactyl\Models\Server $server */ - public function updated(Server $server) + public function updated(Server $server): void { event(new Events\Server\Updated($server)); } diff --git a/app/Observers/SubuserObserver.php b/app/Observers/SubuserObserver.php index 009e65290..efb24d947 100644 --- a/app/Observers/SubuserObserver.php +++ b/app/Observers/SubuserObserver.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Observers; @@ -18,52 +11,44 @@ class SubuserObserver { /** * Listen to the Subuser creating event. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function creating(Subuser $subuser) + public function creating(Subuser $subuser): void { event(new Events\Subuser\Creating($subuser)); } /** * Listen to the Subuser created event. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function created(Subuser $subuser) + public function created(Subuser $subuser): void { event(new Events\Subuser\Created($subuser)); - $subuser->user->notify((new AddedToServer([ - 'user' => $subuser->user->name_first, + $subuser->user->notify(new AddedToServer([ + 'user' => $subuser->user->username, 'name' => $subuser->server->name, 'uuidShort' => $subuser->server->uuidShort, - ]))); + ])); } /** * Listen to the Subuser deleting event. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function deleting(Subuser $subuser) + public function deleting(Subuser $subuser): void { event(new Events\Subuser\Deleting($subuser)); } /** * Listen to the Subuser deleted event. - * - * @param \Pterodactyl\Models\Subuser $subuser */ - public function deleted(Subuser $subuser) + public function deleted(Subuser $subuser): void { event(new Events\Subuser\Deleted($subuser)); - $subuser->user->notify((new RemovedFromServer([ - 'user' => $subuser->user->name_first, + $subuser->user->notify(new RemovedFromServer([ + 'user' => $subuser->user->username, 'name' => $subuser->server->name, - ]))); + ])); } } diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index dd29d908b..dd017a22d 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -7,44 +7,36 @@ use Pterodactyl\Models\User; class UserObserver { - protected $uuid; + protected string $uuid; /** * Listen to the User creating event. - * - * @param \Pterodactyl\Models\User $user */ - public function creating(User $user) + public function creating(User $user): void { event(new Events\User\Creating($user)); } /** * Listen to the User created event. - * - * @param \Pterodactyl\Models\User $user */ - public function created(User $user) + public function created(User $user): void { event(new Events\User\Created($user)); } /** * Listen to the User deleting event. - * - * @param \Pterodactyl\Models\User $user */ - public function deleting(User $user) + public function deleting(User $user): void { event(new Events\User\Deleting($user)); } /** * Listen to the User deleted event. - * - * @param \Pterodactyl\Models\User $user */ - public function deleted(User $user) + public function deleted(User $user): void { event(new Events\User\Deleted($user)); } diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 9b4db6f05..e3565bb25 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -1,16 +1,7 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Policies; -use Cache; -use Carbon; use Pterodactyl\Models\User; use Pterodactyl\Models\Server; @@ -18,32 +9,21 @@ class ServerPolicy { /** * Checks if the user has the given permission on/for the server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @param string $permission - * @return bool */ - protected function checkPermission(User $user, Server $server, $permission) + protected function checkPermission(User $user, Server $server, string $permission): bool { - $permissions = Cache::remember('ServerPolicy.' . $user->uuid . $server->uuid, Carbon::now()->addSeconds(5), function () use ($user, $server) { - return $user->permissions()->server($server)->get()->transform(function ($item) { - return $item->permission; - })->values(); - }); + $subuser = $server->subusers->where('user_id', $user->id)->first(); + if (!$subuser || empty($permission)) { + return false; + } - return $permissions->search($permission, true) !== false; + return in_array($permission, $subuser->permissions); } /** * Runs before any of the functions are called. Used to determine if user is root admin, if so, ignore permissions. - * - * @param \Pterodactyl\Models\User $user - * @param string $ability - * @param \Pterodactyl\Models\Server $server - * @return bool */ - public function before(User $user, $ability, Server $server) + public function before(User $user, string $ability, Server $server): bool { if ($user->root_admin || $server->owner_id === $user->id) { return true; @@ -56,11 +36,8 @@ class ServerPolicy * This is a horrendous hack to avoid Laravel's "smart" behavior that does * not call the before() function if there isn't a function matching the * policy permission. - * - * @param string $name - * @param mixed $arguments */ - public function __call($name, $arguments) + public function __call(string $name, mixed $arguments) { // do nothing } diff --git a/app/Providers/ActivityLogServiceProvider.php b/app/Providers/ActivityLogServiceProvider.php new file mode 100644 index 000000000..7a3de759c --- /dev/null +++ b/app/Providers/ActivityLogServiceProvider.php @@ -0,0 +1,20 @@ +app->scoped(ActivityLogBatchService::class); + $this->app->scoped(ActivityLogTargetableService::class); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1adede6c6..f5e39acf3 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,75 +2,58 @@ namespace Pterodactyl\Providers; -use View; -use Cache; -use Pterodactyl\Models\User; -use Pterodactyl\Models\Server; -use Pterodactyl\Models\Subuser; +use Pterodactyl\Models; +use Illuminate\Support\Str; +use Illuminate\Support\Facades\URL; +use Illuminate\Pagination\Paginator; use Illuminate\Support\Facades\Schema; -use Igaster\LaravelTheme\Facades\Theme; use Illuminate\Support\ServiceProvider; -use Pterodactyl\Observers\UserObserver; -use Pterodactyl\Observers\ServerObserver; -use Pterodactyl\Observers\SubuserObserver; +use Illuminate\Database\Eloquent\Relations\Relation; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. */ - public function boot() + public function boot(): void { Schema::defaultStringLength(191); - User::observe(UserObserver::class); - Server::observe(ServerObserver::class); - Subuser::observe(SubuserObserver::class); + Paginator::useBootstrap(); - View::share('appVersion', $this->versionData()['version'] ?? 'undefined'); - View::share('appIsGit', $this->versionData()['is_git'] ?? false); - Theme::setSetting('cache-version', md5($this->versionData()['version'] ?? 'undefined')); + // If the APP_URL value is set with https:// make sure we force it here. Theoretically + // this should just work with the proxy logic, but there are a lot of cases where it + // doesn't, and it triggers a lot of support requests, so lets just head it off here. + // + // @see https://github.com/pterodactyl/panel/issues/3623 + if (Str::startsWith(config('app.url') ?? '', 'https://')) { + URL::forceScheme('https'); + } + + Relation::enforceMorphMap([ + 'allocation' => Models\Allocation::class, + 'api_key' => Models\ApiKey::class, + 'backup' => Models\Backup::class, + 'database' => Models\Database::class, + 'egg' => Models\Egg::class, + 'egg_variable' => Models\EggVariable::class, + 'schedule' => Models\Schedule::class, + 'server' => Models\Server::class, + 'ssh_key' => Models\UserSSHKey::class, + 'task' => Models\Task::class, + 'user' => Models\User::class, + ]); } /** * Register application service providers. */ - public function register() + public function register(): void { // Only load the settings service provider if the environment // is configured to allow it. - if (! config('pterodactyl.load_environment_only', false) && $this->app->environment() !== 'testing') { + if (!config('pterodactyl.load_environment_only', false) && $this->app->environment() !== 'testing') { $this->app->register(SettingsServiceProvider::class); } } - - /** - * Return version information for the footer. - * - * @return array - */ - protected function versionData() - { - return Cache::remember('git-version', 5, function () { - if (file_exists(base_path('.git/HEAD'))) { - $head = explode(' ', file_get_contents(base_path('.git/HEAD'))); - - if (array_key_exists(1, $head)) { - $path = base_path('.git/' . trim($head[1])); - } - } - - if (isset($path) && file_exists($path)) { - return [ - 'version' => substr(file_get_contents($path), 0, 8), - 'is_git' => true, - ]; - } - - return [ - 'version' => config('app.version'), - 'is_git' => false, - ]; - }); - } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index e147736ed..9f9556b9a 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,24 +2,28 @@ namespace Pterodactyl\Providers; +use Laravel\Sanctum\Sanctum; +use Pterodactyl\Models\ApiKey; +use Pterodactyl\Models\Server; +use Pterodactyl\Policies\ServerPolicy; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** - * The policy mappings for the application. - * - * @var array + * The model to policy mappings for the application. */ protected $policies = [ - 'Pterodactyl\Models\Server' => 'Pterodactyl\Policies\ServerPolicy', + Server::class => ServerPolicy::class, ]; - /** - * Register any application authentication / authorization services. - */ - public function boot() + public function boot(): void { - $this->registerPolicies(); + Sanctum::usePersonalAccessTokenModel(ApiKey::class); + } + + public function register(): void + { + Sanctum::ignoreMigrations(); } } diff --git a/app/Providers/BackupsServiceProvider.php b/app/Providers/BackupsServiceProvider.php new file mode 100644 index 000000000..25ce9bd7a --- /dev/null +++ b/app/Providers/BackupsServiceProvider.php @@ -0,0 +1,25 @@ +app->singleton(BackupManager::class, function ($app) { + return new BackupManager($app); + }); + } + + public function provides(): array + { + return [BackupManager::class]; + } +} diff --git a/app/Providers/BladeServiceProvider.php b/app/Providers/BladeServiceProvider.php index 97b0df48e..3512acfd9 100644 --- a/app/Providers/BladeServiceProvider.php +++ b/app/Providers/BladeServiceProvider.php @@ -9,11 +9,11 @@ class BladeServiceProvider extends ServiceProvider /** * Perform post-registration booting of services. */ - public function boot() + public function boot(): void { $this->app->make('blade.compiler') ->directive('datetimeHuman', function ($expression) { - return "setTimezone(config('app.timezone'))->toDateTimeString(); ?>"; + return "setTimezone(config('app.timezone'))->toDateTimeString(); ?>"; }); } } diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php index 3f7c84be4..0630a6377 100644 --- a/app/Providers/BroadcastServiceProvider.php +++ b/app/Providers/BroadcastServiceProvider.php @@ -10,7 +10,7 @@ class BroadcastServiceProvider extends ServiceProvider /** * Bootstrap any application services. */ - public function boot() + public function boot(): void { Broadcast::routes(); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 5be9601d6..6f91f24f5 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,6 +2,15 @@ namespace Pterodactyl\Providers; +use Pterodactyl\Models\User; +use Pterodactyl\Models\Server; +use Pterodactyl\Models\Subuser; +use Pterodactyl\Models\EggVariable; +use Pterodactyl\Observers\UserObserver; +use Pterodactyl\Observers\ServerObserver; +use Pterodactyl\Observers\SubuserObserver; +use Pterodactyl\Observers\EggVariableObserver; +use Pterodactyl\Listeners\Auth\AuthenticationListener; use Pterodactyl\Events\Server\Installed as ServerInstalledEvent; use Pterodactyl\Notifications\ServerInstalled as ServerInstalledNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -9,13 +18,26 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi class EventServiceProvider extends ServiceProvider { /** - * The event listener mappings for the application. - * - * @var array + * The event to listener mappings for the application. */ protected $listen = [ - ServerInstalledEvent::class => [ - ServerInstalledNotification::class, - ], + ServerInstalledEvent::class => [ServerInstalledNotification::class], ]; + + protected $subscribe = [ + AuthenticationListener::class, + ]; + + /** + * Register any events for your application. + */ + public function boot(): void + { + parent::boot(); + + User::observe(UserObserver::class); + Server::observe(ServerObserver::class); + Subuser::observe(SubuserObserver::class); + EggVariable::observe(EggVariableObserver::class); + } } diff --git a/app/Providers/HashidsServiceProvider.php b/app/Providers/HashidsServiceProvider.php index f2b3935d8..6722f8dec 100644 --- a/app/Providers/HashidsServiceProvider.php +++ b/app/Providers/HashidsServiceProvider.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Providers; @@ -18,16 +11,13 @@ class HashidsServiceProvider extends ServiceProvider /** * Register the ability to use Hashids. */ - public function register() + public function register(): void { $this->app->singleton(HashidsInterface::class, function () { - /** @var \Illuminate\Contracts\Config\Repository $config */ - $config = $this->app['config']; - return new Hashids( - $config->get('hashids.salt', ''), - $config->get('hashids.length', 0), - $config->get('hashids.alphabet', 'abcdefghijkmlnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890') + config('hashids.salt', ''), + config('hashids.length', 0), + config('hashids.alphabet', 'abcdefghijkmlnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890') ); }); diff --git a/app/Providers/MacroServiceProvider.php b/app/Providers/MacroServiceProvider.php deleted file mode 100644 index 9eae42b81..000000000 --- a/app/Providers/MacroServiceProvider.php +++ /dev/null @@ -1,28 +0,0 @@ - 0.9) { - $size = $size / 1024; - $i++; - } - - return round($size, ($i < 2) ? 0 : $precision) . ' ' . $units[$i]; - }); - } -} diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php index aa5fbbaa5..b06ca7a13 100644 --- a/app/Providers/RepositoryServiceProvider.php +++ b/app/Providers/RepositoryServiceProvider.php @@ -3,15 +3,11 @@ namespace Pterodactyl\Providers; use Illuminate\Support\ServiceProvider; -use Pterodactyl\Repositories\Daemon\FileRepository; -use Pterodactyl\Repositories\Daemon\PowerRepository; use Pterodactyl\Repositories\Eloquent\EggRepository; use Pterodactyl\Repositories\Eloquent\NestRepository; use Pterodactyl\Repositories\Eloquent\NodeRepository; -use Pterodactyl\Repositories\Eloquent\PackRepository; use Pterodactyl\Repositories\Eloquent\TaskRepository; use Pterodactyl\Repositories\Eloquent\UserRepository; -use Pterodactyl\Repositories\Daemon\CommandRepository; use Pterodactyl\Repositories\Eloquent\ApiKeyRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Eloquent\SessionRepository; @@ -20,15 +16,11 @@ use Pterodactyl\Repositories\Eloquent\DatabaseRepository; use Pterodactyl\Repositories\Eloquent\LocationRepository; use Pterodactyl\Repositories\Eloquent\ScheduleRepository; use Pterodactyl\Repositories\Eloquent\SettingsRepository; -use Pterodactyl\Repositories\Eloquent\DaemonKeyRepository; use Pterodactyl\Repositories\Eloquent\AllocationRepository; -use Pterodactyl\Repositories\Eloquent\PermissionRepository; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Repositories\Daemon\ConfigurationRepository; use Pterodactyl\Repositories\Eloquent\EggVariableRepository; use Pterodactyl\Contracts\Repository\NestRepositoryInterface; use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; -use Pterodactyl\Contracts\Repository\PackRepositoryInterface; use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository; @@ -41,30 +33,21 @@ use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; -use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface; -use Pterodactyl\Repositories\Daemon\ServerRepository as DaemonServerRepository; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class RepositoryServiceProvider extends ServiceProvider { /** - * Register all of the repository bindings. + * Register all the repository bindings. */ - public function register() + public function register(): void { // Eloquent Repositories $this->app->bind(AllocationRepositoryInterface::class, AllocationRepository::class); $this->app->bind(ApiKeyRepositoryInterface::class, ApiKeyRepository::class); - $this->app->bind(DaemonKeyRepositoryInterface::class, DaemonKeyRepository::class); $this->app->bind(DatabaseRepositoryInterface::class, DatabaseRepository::class); $this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class); $this->app->bind(EggRepositoryInterface::class, EggRepository::class); @@ -72,8 +55,6 @@ class RepositoryServiceProvider extends ServiceProvider $this->app->bind(LocationRepositoryInterface::class, LocationRepository::class); $this->app->bind(NestRepositoryInterface::class, NestRepository::class); $this->app->bind(NodeRepositoryInterface::class, NodeRepository::class); - $this->app->bind(PackRepositoryInterface::class, PackRepository::class); - $this->app->bind(PermissionRepositoryInterface::class, PermissionRepository::class); $this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class); $this->app->bind(ServerRepositoryInterface::class, ServerRepository::class); $this->app->bind(ServerVariableRepositoryInterface::class, ServerVariableRepository::class); @@ -82,12 +63,5 @@ class RepositoryServiceProvider extends ServiceProvider $this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class); $this->app->bind(TaskRepositoryInterface::class, TaskRepository::class); $this->app->bind(UserRepositoryInterface::class, UserRepository::class); - - // Daemon Repositories - $this->app->bind(ConfigurationRepositoryInterface::class, ConfigurationRepository::class); - $this->app->bind(CommandRepositoryInterface::class, CommandRepository::class); - $this->app->bind(DaemonServerRepositoryInterface::class, DaemonServerRepository::class); - $this->app->bind(FileRepositoryInterface::class, FileRepository::class); - $this->app->bind(PowerRepositoryInterface::class, PowerRepository::class); } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index f0e978116..ac7d52902 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,55 +2,109 @@ namespace Pterodactyl\Providers; +use Illuminate\Http\Request; +use Pterodactyl\Models\Database; use Illuminate\Support\Facades\Route; +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Support\Facades\RateLimiter; +use Pterodactyl\Http\Middleware\TrimStrings; +use Pterodactyl\Http\Middleware\AdminAuthenticate; +use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; class RouteServiceProvider extends ServiceProvider { - /** - * This namespace is applied to the controller routes in your routes file. - * - * In addition, it is set as the URL generator's root namespace. - * - * @var string - */ - protected $namespace = 'Pterodactyl\Http\Controllers'; + protected const FILE_PATH_REGEX = '/^\/api\/client\/servers\/([a-z0-9-]{36})\/files(\/?$|\/(.)*$)/i'; /** - * Define the routes for the application. + * Define your route model bindings, pattern filters, etc. */ - public function map() + public function boot(): void { - Route::middleware(['web', 'auth', 'csrf']) - ->namespace($this->namespace . '\Base') - ->group(base_path('routes/base.php')); + $this->configureRateLimiting(); - Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin') - ->namespace($this->namespace . '\Admin') - ->group(base_path('routes/admin.php')); + // Disable trimming string values when requesting file information — it isn't helpful + // and messes up the ability to actually open a directory that ends with a space. + TrimStrings::skipWhen(function (Request $request) { + return preg_match(self::FILE_PATH_REGEX, $request->getPathInfo()) === 1; + }); - Route::middleware(['web', 'csrf'])->prefix('/auth') - ->namespace($this->namespace . '\Auth') - ->group(base_path('routes/auth.php')); + // This is needed to make use of the "resolveRouteBinding" functionality in the + // model. Without it, you'll never trigger that logic flow thus resulting in a 404 + // error because we request databases with a HashID, and not with a normal ID. + Route::model('database', Database::class); - Route::middleware(['web', 'csrf', 'auth', 'server', 'subuser.auth', 'node.maintenance'])->prefix('/server/{server}') - ->namespace($this->namespace . '\Server') - ->group(base_path('routes/server.php')); + $this->routes(function () { + Route::middleware('web')->group(function () { + Route::middleware(['auth.session', RequireTwoFactorAuthentication::class]) + ->group(base_path('routes/base.php')); - Route::middleware(['api'])->prefix('/api/application') - ->namespace($this->namespace . '\Api\Application') - ->group(base_path('routes/api-application.php')); + Route::middleware(['auth.session', RequireTwoFactorAuthentication::class, AdminAuthenticate::class]) + ->prefix('/admin') + ->group(base_path('routes/admin.php')); - Route::middleware(['client-api'])->prefix('/api/client') - ->namespace($this->namespace . '\Api\Client') - ->group(base_path('routes/api-client.php')); + Route::middleware('guest')->prefix('/auth')->group(base_path('routes/auth.php')); + }); - Route::middleware(['daemon'])->prefix('/api/remote') - ->namespace($this->namespace . '\Api\Remote') - ->group(base_path('routes/api-remote.php')); + Route::middleware(['api', RequireTwoFactorAuthentication::class])->group(function () { + Route::middleware(['application-api', 'throttle:api.application']) + ->prefix('/api/application') + ->scopeBindings() + ->group(base_path('routes/api-application.php')); - Route::middleware(['web', 'daemon-old'])->prefix('/daemon') - ->namespace($this->namespace . '\Daemon') - ->group(base_path('routes/daemon.php')); + Route::middleware(['client-api', 'throttle:api.client']) + ->prefix('/api/client') + ->scopeBindings() + ->group(base_path('routes/api-client.php')); + }); + + Route::middleware('daemon') + ->prefix('/api/remote') + ->scopeBindings() + ->group(base_path('routes/api-remote.php')); + }); + } + + /** + * Configure the rate limiters for the application. + */ + protected function configureRateLimiting(): void + { + // Authentication rate limiting. For login and checkpoint endpoints we'll apply + // a limit of 10 requests per minute, for the forgot password endpoint apply a + // limit of two per minute for the requester so that there is less ability to + // trigger email spam. + RateLimiter::for('authentication', function (Request $request) { + if ($request->route()->named('auth.post.forgot-password')) { + return Limit::perMinute(2)->by($request->ip()); + } + + return Limit::perMinute(10); + }); + + // Configure the throttles for both the application and client APIs below. + // This is configurable per-instance in "config/http.php". By default this + // limiter will be tied to the specific request user, and falls back to the + // request IP if there is no request user present for the key. + // + // This means that an authenticated API user cannot use IP switching to get + // around the limits. + RateLimiter::for('api.client', function (Request $request) { + $key = optional($request->user())->uuid ?: $request->ip(); + + return Limit::perMinutes( + config('http.rate_limit.client_period'), + config('http.rate_limit.client') + )->by($key); + }); + + RateLimiter::for('api.application', function (Request $request) { + $key = optional($request->user())->uuid ?: $request->ip(); + + return Limit::perMinutes( + config('http.rate_limit.application_period'), + config('http.rate_limit.application') + )->by($key); + }); } } diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php index 2cc26e663..e463961cf 100644 --- a/app/Providers/SettingsServiceProvider.php +++ b/app/Providers/SettingsServiceProvider.php @@ -15,10 +15,8 @@ class SettingsServiceProvider extends ServiceProvider /** * An array of configuration keys to override with database values * if they exist. - * - * @var array */ - protected $keys = [ + protected array $keys = [ 'app:name', 'app:locale', 'recaptcha:enabled', @@ -29,47 +27,41 @@ class SettingsServiceProvider extends ServiceProvider 'pterodactyl:console:count', 'pterodactyl:console:frequency', 'pterodactyl:auth:2fa_required', + 'pterodactyl:client_features:allocations:enabled', + 'pterodactyl:client_features:allocations:range_start', + 'pterodactyl:client_features:allocations:range_end', ]; /** * Keys specific to the mail driver that are only grabbed from the database * when using the SMTP driver. - * - * @var array */ - protected $emailKeys = [ - 'mail:host', - 'mail:port', + protected array $emailKeys = [ + 'mail:mailers:smtp:host', + 'mail:mailers:smtp:port', + 'mail:mailers:smtp:encryption', + 'mail:mailers:smtp:username', + 'mail:mailers:smtp:password', 'mail:from:address', 'mail:from:name', - 'mail:encryption', - 'mail:username', - 'mail:password', ]; /** * Keys that are encrypted and should be decrypted when set in the * configuration array. - * - * @var array */ - protected static $encrypted = [ - 'mail:password', + protected static array $encrypted = [ + 'mail:mailers:smtp:password', ]; /** * Boot the service provider. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - * @param \Psr\Log\LoggerInterface $log - * @param \Pterodactyl\Contracts\Repository\SettingsRepositoryInterface $settings */ - public function boot(ConfigRepository $config, Encrypter $encrypter, Log $log, SettingsRepositoryInterface $settings) + public function boot(ConfigRepository $config, Encrypter $encrypter, Log $log, SettingsRepositoryInterface $settings): void { // Only set the email driver settings from the database if we // are configured using SMTP as the driver. - if ($config->get('mail.driver') === 'smtp') { + if ($config->get('mail.default') === 'smtp') { $this->keys = array_merge($this->keys, $this->emailKeys); } @@ -88,7 +80,7 @@ class SettingsServiceProvider extends ServiceProvider if (in_array($key, self::$encrypted)) { try { $value = $encrypter->decrypt($value); - } catch (DecryptException $exception) { + } catch (DecryptException) { } } @@ -114,9 +106,6 @@ class SettingsServiceProvider extends ServiceProvider } } - /** - * @return array - */ public static function getEncryptedKeys(): array { return self::$encrypted; diff --git a/app/Providers/ViewComposerServiceProvider.php b/app/Providers/ViewComposerServiceProvider.php index ab8c9e164..8ab7208c7 100644 --- a/app/Providers/ViewComposerServiceProvider.php +++ b/app/Providers/ViewComposerServiceProvider.php @@ -3,19 +3,15 @@ namespace Pterodactyl\Providers; use Illuminate\Support\ServiceProvider; -use Pterodactyl\Http\ViewComposers\ServerListComposer; -use Pterodactyl\Http\ViewComposers\Server\ServerDataComposer; +use Pterodactyl\Http\ViewComposers\AssetComposer; class ViewComposerServiceProvider extends ServiceProvider { /** * Register bindings in the container. */ - public function boot() + public function boot(): void { - $this->app->make('view')->composer('server.*', ServerDataComposer::class); - - // Add data to make the sidebar work when viewing a server. - $this->app->make('view')->composer(['server.*'], ServerListComposer::class); + $this->app->make('view')->composer('*', AssetComposer::class); } } diff --git a/app/Repositories/Concerns/Searchable.php b/app/Repositories/Concerns/Searchable.php deleted file mode 100644 index 26ed6544a..000000000 --- a/app/Repositories/Concerns/Searchable.php +++ /dev/null @@ -1,64 +0,0 @@ -setSearchTerm($term); - } - - /** - * Set the search term to use when requesting all records from - * the model. - * - * @param string|null $term - * @return $this - */ - public function setSearchTerm(string $term = null) - { - if (empty($term)) { - return $this; - } - - $clone = clone $this; - $clone->searchTerm = $term; - - return $clone; - } - - /** - * Determine if a valid search term is set on this repository. - * - * @return bool - */ - public function hasSearchTerm(): bool - { - return ! empty($this->searchTerm); - } - - /** - * Return the search term. - * - * @return string|null - */ - public function getSearchTerm() - { - return $this->searchTerm; - } -} diff --git a/app/Repositories/Daemon/BaseRepository.php b/app/Repositories/Daemon/BaseRepository.php deleted file mode 100644 index 186b5917d..000000000 --- a/app/Repositories/Daemon/BaseRepository.php +++ /dev/null @@ -1,154 +0,0 @@ -app = $app; - $this->nodeRepository = $nodeRepository; - } - - /** - * Set the node model to be used for this daemon connection. - * - * @param \Pterodactyl\Models\Node $node - * @return $this - */ - public function setNode(Node $node) - { - $this->node = $node; - - return $this; - } - - /** - * Return the node model being used. - * - * @return \Pterodactyl\Models\Node|null - */ - public function getNode() - { - return $this->node; - } - - /** - * Set the Server model to use when requesting information from the Daemon. - * - * @param \Pterodactyl\Models\Server $server - * @return $this - */ - public function setServer(Server $server) - { - $this->server = $server; - - return $this; - } - - /** - * Return the Server model. - * - * @return \Pterodactyl\Models\Server|null - */ - public function getServer() - { - return $this->server; - } - - /** - * Set the token to be used in the X-Access-Token header for requests to the daemon. - * - * @param string $token - * @return $this - */ - public function setToken(string $token) - { - $this->token = $token; - - return $this; - } - - /** - * Return the access token being used for requests. - * - * @return string|null - */ - public function getToken() - { - return $this->token; - } - - /** - * Return an instance of the Guzzle HTTP Client to be used for requests. - * - * @param array $headers - * @return \GuzzleHttp\Client - */ - public function getHttpClient(array $headers = []): Client - { - // If no node is set, load the relationship onto the Server model - // and pass that to the setNode function. - if (! $this->getNode() instanceof Node) { - if (! $this->getServer() instanceof Server) { - throw new RuntimeException('An instance of ' . Node::class . ' or ' . Server::class . ' must be set on this repository in order to return a client.'); - } - - $this->getServer()->loadMissing('node'); - $this->setNode($this->getServer()->getRelation('node')); - } - - if ($this->getServer() instanceof Server) { - $headers['X-Access-Server'] = $this->getServer()->uuid; - } - - $headers['X-Access-Token'] = $this->getToken() ?? $this->getNode()->daemonSecret; - - return new Client([ - 'verify' => config('app.env') === 'production', - 'base_uri' => sprintf('%s://%s:%s/v1/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen), - 'timeout' => config('pterodactyl.guzzle.timeout'), - 'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'), - 'headers' => $headers, - ]); - } -} diff --git a/app/Repositories/Daemon/CommandRepository.php b/app/Repositories/Daemon/CommandRepository.php deleted file mode 100644 index cd123cd89..000000000 --- a/app/Repositories/Daemon/CommandRepository.php +++ /dev/null @@ -1,25 +0,0 @@ -getHttpClient()->request('POST', 'server/command', [ - 'json' => [ - 'command' => $command, - ], - ]); - } -} diff --git a/app/Repositories/Daemon/ConfigurationRepository.php b/app/Repositories/Daemon/ConfigurationRepository.php deleted file mode 100644 index 3905335a5..000000000 --- a/app/Repositories/Daemon/ConfigurationRepository.php +++ /dev/null @@ -1,46 +0,0 @@ -getNode(); - $structure = [ - 'web' => [ - 'listen' => $node->daemonListen, - 'ssl' => [ - 'enabled' => (! $node->behind_proxy && $node->scheme === 'https'), - ], - ], - 'sftp' => [ - 'path' => $node->daemonBase, - 'port' => $node->daemonSFTP, - ], - 'remote' => [ - 'base' => config('app.url'), - ], - 'uploads' => [ - 'size_limit' => $node->upload_size, - ], - 'keys' => [ - $node->daemonSecret, - ], - ]; - - return $this->getHttpClient()->request('PATCH', 'config', [ - 'json' => array_merge($structure, $overrides), - ]); - } -} diff --git a/app/Repositories/Daemon/FileRepository.php b/app/Repositories/Daemon/FileRepository.php deleted file mode 100644 index 46117f3c5..000000000 --- a/app/Repositories/Daemon/FileRepository.php +++ /dev/null @@ -1,116 +0,0 @@ -getHttpClient()->request('GET', sprintf( - 'server/file/stat/%s', - rawurlencode($file['dirname'] . $file['basename']) - )); - - return json_decode($response->getBody()); - } - - /** - * Return the contents of a given file if it can be edited in the Panel. - * - * @param string $path - * @return string - * - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function getContent(string $path): string - { - $file = str_replace('\\', '/', pathinfo($path)); - $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; - - $response = $this->getHttpClient()->request('GET', sprintf( - 'server/file/f/%s', - rawurlencode($file['dirname'] . $file['basename']) - )); - - return object_get(json_decode($response->getBody()), 'content'); - } - - /** - * Save new contents to a given file. - * - * @param string $path - * @param string $content - * @return \Psr\Http\Message\ResponseInterface - * - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function putContent(string $path, string $content): ResponseInterface - { - $file = str_replace('\\', '/', pathinfo($path)); - $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; - - return $this->getHttpClient()->request('POST', 'server/file/save', [ - 'json' => [ - 'path' => rawurlencode($file['dirname'] . $file['basename']), - 'content' => $content, - ], - ]); - } - - /** - * Return a directory listing for a given path. - * - * @param string $path - * @return array - * - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function getDirectory(string $path): array - { - $response = $this->getHttpClient()->request('GET', sprintf('server/directory/%s', rawurlencode($path))); - - $contents = json_decode($response->getBody()); - $files = $folders = []; - - foreach ($contents as $value) { - if ($value->directory) { - array_push($folders, [ - 'entry' => $value->name, - 'directory' => trim($path, '/'), - 'size' => null, - 'date' => strtotime($value->modified), - 'mime' => $value->mime, - ]); - } elseif ($value->file) { - array_push($files, [ - 'entry' => $value->name, - 'directory' => trim($path, '/'), - 'extension' => str_replace('\\', '/', pathinfo($value->name, PATHINFO_EXTENSION)), - 'size' => human_readable($value->size), - 'date' => strtotime($value->modified), - 'mime' => $value->mime, - ]); - } - } - - return [ - 'files' => $files, - 'folders' => $folders, - ]; - } -} diff --git a/app/Repositories/Daemon/PowerRepository.php b/app/Repositories/Daemon/PowerRepository.php deleted file mode 100644 index d7ce8d5e5..000000000 --- a/app/Repositories/Daemon/PowerRepository.php +++ /dev/null @@ -1,36 +0,0 @@ -getHttpClient()->request('PUT', 'server/power', [ - 'json' => [ - 'action' => $signal, - ], - ]); - default: - throw new InvalidPowerSignalException('The signal "' . $signal . '" is not defined and could not be processed.'); - } - } -} diff --git a/app/Repositories/Daemon/ServerRepository.php b/app/Repositories/Daemon/ServerRepository.php deleted file mode 100644 index f1bd445fc..000000000 --- a/app/Repositories/Daemon/ServerRepository.php +++ /dev/null @@ -1,134 +0,0 @@ - $value) { - $structure[$key] = value($value); - } - - return $this->getHttpClient()->request('POST', 'servers', [ - 'json' => $structure, - ]); - } - - /** - * Update server details on the daemon. - * - * @param array $data - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function update(array $data): ResponseInterface - { - return $this->getHttpClient()->request('PATCH', 'server', [ - 'json' => $data, - ]); - } - - /** - * Mark a server to be reinstalled on the system. - * - * @param array|null $data - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function reinstall(array $data = null): ResponseInterface - { - return $this->getHttpClient()->request('POST', 'server/reinstall', [ - 'json' => $data ?? [], - ]); - } - - /** - * Mark a server as needing a container rebuild the next time the server is booted. - * - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function rebuild(): ResponseInterface - { - return $this->getHttpClient()->request('POST', 'server/rebuild'); - } - - /** - * Suspend a server on the daemon. - * - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function suspend(): ResponseInterface - { - return $this->getHttpClient()->request('POST', 'server/suspend'); - } - - /** - * Un-suspend a server on the daemon. - * - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function unsuspend(): ResponseInterface - { - return $this->getHttpClient()->request('POST', 'server/unsuspend'); - } - - /** - * Delete a server on the daemon. - * - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function delete(): ResponseInterface - { - return $this->getHttpClient()->request('DELETE', 'servers'); - } - - /** - * Return details on a specific server. - * - * @return \Psr\Http\Message\ResponseInterface - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function details(): ResponseInterface - { - return $this->getHttpClient()->request('GET', 'server'); - } - - /** - * Revoke an access key on the daemon before the time is expired. - * - * @param string|array $key - * @return \Psr\Http\Message\ResponseInterface - * - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function revokeAccessKey($key): ResponseInterface - { - if (is_array($key)) { - return $this->getHttpClient()->request('POST', 'keys/batch-delete', [ - 'json' => ['keys' => $key], - ]); - } - - Assert::stringNotEmpty($key, 'First argument passed to revokeAccessKey must be a non-empty string or array, received %s.'); - - return $this->getHttpClient()->request('DELETE', 'keys/' . $key); - } -} diff --git a/app/Repositories/Eloquent/AllocationRepository.php b/app/Repositories/Eloquent/AllocationRepository.php index a9721ac09..01ed1c5be 100644 --- a/app/Repositories/Eloquent/AllocationRepository.php +++ b/app/Repositories/Eloquent/AllocationRepository.php @@ -2,98 +2,32 @@ namespace Pterodactyl\Repositories\Eloquent; -use Illuminate\Support\Collection; use Pterodactyl\Models\Allocation; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; class AllocationRepository extends EloquentRepository implements AllocationRepositoryInterface { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Allocation::class; } /** - * Set an array of allocation IDs to be assigned to a specific server. - * - * @param int|null $server - * @param array $ids - * @return int - */ - public function assignAllocationsToServer(int $server = null, array $ids): int - { - return $this->getBuilder()->whereIn('id', $ids)->update(['server_id' => $server]); - } - - /** - * Return all of the allocations for a specific node. - * - * @param int $node - * @return \Illuminate\Support\Collection - */ - public function getAllocationsForNode(int $node): Collection - { - return $this->getBuilder()->where('node_id', $node)->get($this->getColumns()); - } - - /** - * Return all of the allocations for a node in a paginated format. - * - * @param int $node - * @param int $perPage - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getPaginatedAllocationsForNode(int $node, int $perPage = 100): LengthAwarePaginator - { - return $this->getBuilder()->where('node_id', $node)->paginate($perPage, $this->getColumns()); - } - - /** - * Return all of the unique IPs that exist for a given node. - * - * @param int $node - * @return \Illuminate\Support\Collection - */ - public function getUniqueAllocationIpsForNode(int $node): Collection - { - return $this->getBuilder()->where('node_id', $node) - ->groupBy('ip') - ->orderByRaw('INET_ATON(ip) ASC') - ->get($this->getColumns()); - } - - /** - * Return all of the allocations that exist for a node that are not currently + * Return all the allocations that exist for a node that are not currently * allocated. - * - * @param int $node - * @return array */ public function getUnassignedAllocationIds(int $node): array { - $results = $this->getBuilder()->select('id')->whereNull('server_id')->where('node_id', $node)->get(); - - return $results->pluck('id')->toArray(); - } - - /** - * Get an array of all allocations that are currently assigned to a given server. - * - * @param int $server - * @return array - */ - public function getAssignedAllocationIds(int $server): array - { - $results = $this->getBuilder()->select('id')->where('server_id', $server)->get(); - - return $results->pluck('id')->toArray(); + return Allocation::query()->select('id') + ->whereNull('server_id') + ->where('node_id', $node) + ->get() + ->pluck('id') + ->toArray(); } /** @@ -103,57 +37,47 @@ class AllocationRepository extends EloquentRepository implements AllocationRepos * * If an array of nodes is passed the results will be limited to allocations * in those nodes. - * - * @param array $nodes - * @return array */ - public function getDiscardableDedicatedAllocations(array $nodes = []): array + protected function getDiscardableDedicatedAllocations(array $nodes = []): array { - $instance = $this->getBuilder()->select( - $this->getBuilder()->raw('CONCAT_WS("-", node_id, ip) as result') - ); + $query = Allocation::query()->selectRaw('CONCAT_WS(\'-\', node_id, ip) as result'); - if (! empty($nodes)) { - $instance->whereIn('node_id', $nodes); + if (!empty($nodes)) { + $query->whereIn('node_id', $nodes); } - $results = $instance->whereNotNull('server_id') - ->groupBy($this->getBuilder()->raw('CONCAT(node_id, ip)')) - ->get(); - - return $results->pluck('result')->toArray(); + return $query->whereNotNull('server_id') + ->groupByRaw('result') + ->get() + ->pluck('result') + ->toArray(); } /** * Return a single allocation from those meeting the requirements. - * - * @param array $nodes - * @param array $ports - * @param bool $dedicated - * @return \Pterodactyl\Models\Allocation|null */ - public function getRandomAllocation(array $nodes, array $ports, bool $dedicated = false) + public function getRandomAllocation(array $nodes, array $ports, bool $dedicated = false): ?Allocation { - $instance = $this->getBuilder()->whereNull('server_id'); + $query = Allocation::query()->whereNull('server_id'); - if (! empty($nodes)) { - $instance->whereIn('node_id', $nodes); + if (!empty($nodes)) { + $query->whereIn('node_id', $nodes); } - if (! empty($ports)) { - $instance->where(function (Builder $query) use ($ports) { + if (!empty($ports)) { + $query->where(function (Builder $inner) use ($ports) { $whereIn = []; foreach ($ports as $port) { if (is_array($port)) { - $query->orWhereBetween('port', $port); + $inner->orWhereBetween('port', $port); continue; } $whereIn[] = $port; } - if (! empty($whereIn)) { - $query->orWhereIn('port', $whereIn); + if (!empty($whereIn)) { + $inner->orWhereIn('port', $whereIn); } }); } @@ -163,13 +87,14 @@ class AllocationRepository extends EloquentRepository implements AllocationRepos if ($dedicated) { $discard = $this->getDiscardableDedicatedAllocations($nodes); - if (! empty($discard)) { - $instance->whereNotIn( - $this->getBuilder()->raw('CONCAT_WS("-", node_id, ip)'), $discard + if (!empty($discard)) { + $query->whereNotIn( + $this->getBuilder()->raw('CONCAT_WS(\'-\', node_id, ip)'), + $discard ); } } - return $instance->inRandomOrder()->first(); + return $query->inRandomOrder()->first(); } } diff --git a/app/Repositories/Eloquent/ApiKeyRepository.php b/app/Repositories/Eloquent/ApiKeyRepository.php index 7ba0c9982..eb1a362ae 100644 --- a/app/Repositories/Eloquent/ApiKeyRepository.php +++ b/app/Repositories/Eloquent/ApiKeyRepository.php @@ -11,19 +11,14 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return ApiKey::class; } /** - * Get all of the account API keys that exist for a specific user. - * - * @param \Pterodactyl\Models\User $user - * @return \Illuminate\Support\Collection + * Get all the account API keys that exist for a specific user. */ public function getAccountKeys(User $user): Collection { @@ -33,10 +28,7 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt } /** - * Get all of the application API keys that exist for a specific user. - * - * @param \Pterodactyl\Models\User $user - * @return \Illuminate\Support\Collection + * Get all the application API keys that exist for a specific user. */ public function getApplicationKeys(User $user): Collection { @@ -47,10 +39,6 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt /** * Delete an account API key from the panel for a specific user. - * - * @param \Pterodactyl\Models\User $user - * @param string $identifier - * @return int */ public function deleteAccountKey(User $user, string $identifier): int { @@ -62,10 +50,6 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt /** * Delete an application API key from the panel for a specific user. - * - * @param \Pterodactyl\Models\User $user - * @param string $identifier - * @return int */ public function deleteApplicationKey(User $user, string $identifier): int { diff --git a/app/Repositories/Eloquent/BackupRepository.php b/app/Repositories/Eloquent/BackupRepository.php new file mode 100644 index 000000000..bbc5d2cd9 --- /dev/null +++ b/app/Repositories/Eloquent/BackupRepository.php @@ -0,0 +1,45 @@ +getBuilder() + ->withTrashed() + ->where('server_id', $server) + ->where(function ($query) { + $query->whereNull('completed_at') + ->orWhere('is_successful', '=', true); + }) + ->where('created_at', '>=', Carbon::now()->subSeconds($seconds)->toDateTimeString()) + ->get() + ->toBase(); + } + + /** + * Returns a query filtering only non-failed backups for a specific server. + */ + public function getNonFailedBackups(Server $server): HasMany + { + return $server->backups()->where(function ($query) { + $query->whereNull('completed_at') + ->orWhere('is_successful', true); + }); + } +} diff --git a/app/Repositories/Eloquent/DaemonKeyRepository.php b/app/Repositories/Eloquent/DaemonKeyRepository.php deleted file mode 100644 index c53f8a471..000000000 --- a/app/Repositories/Eloquent/DaemonKeyRepository.php +++ /dev/null @@ -1,87 +0,0 @@ -relationLoaded('server') || $refresh) { - $key->load('server'); - } - - if (! $key->relationLoaded('user') || $refresh) { - $key->load('user'); - } - - return $key; - } - - /** - * Return a daemon key with the associated server relation attached. - * - * @param string $key - * @return \Pterodactyl\Models\DaemonKey - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getKeyWithServer(string $key): DaemonKey - { - Assert::notEmpty($key, 'Expected non-empty string as first argument passed to ' . __METHOD__); - - try { - return $this->getBuilder()->with('server')->where('secret', '=', $key)->firstOrFail($this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; - } - } - - /** - * Get all of the keys for a specific user including the information needed - * from their server relation for revocation on the daemon. - * - * @param \Pterodactyl\Models\User $user - * @return \Illuminate\Support\Collection - */ - public function getKeysForRevocation(User $user): Collection - { - return $this->getBuilder()->with('node')->where('user_id', $user->id)->get($this->getColumns()); - } - - /** - * Delete an array of daemon keys from the database. Used primarily in - * conjunction with getKeysForRevocation. - * - * @param array $ids - * @return bool|int - */ - public function deleteKeys(array $ids) - { - return $this->getBuilder()->whereIn('id', $ids)->delete(); - } -} diff --git a/app/Repositories/Eloquent/DatabaseHostRepository.php b/app/Repositories/Eloquent/DatabaseHostRepository.php index 4ed4d411e..2c2b9dcde 100644 --- a/app/Repositories/Eloquent/DatabaseHostRepository.php +++ b/app/Repositories/Eloquent/DatabaseHostRepository.php @@ -10,10 +10,8 @@ class DatabaseHostRepository extends EloquentRepository implements DatabaseHostR { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return DatabaseHost::class; } @@ -21,8 +19,6 @@ class DatabaseHostRepository extends EloquentRepository implements DatabaseHostR /** * Return database hosts with a count of databases and the node * information for which it is attached. - * - * @return \Illuminate\Support\Collection */ public function getWithViewDetails(): Collection { diff --git a/app/Repositories/Eloquent/DatabaseRepository.php b/app/Repositories/Eloquent/DatabaseRepository.php index 9f78efb7a..9bc33d035 100644 --- a/app/Repositories/Eloquent/DatabaseRepository.php +++ b/app/Repositories/Eloquent/DatabaseRepository.php @@ -8,60 +8,29 @@ use Illuminate\Foundation\Application; use Illuminate\Database\DatabaseManager; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; -use Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException; class DatabaseRepository extends EloquentRepository implements DatabaseRepositoryInterface { - /** - * @var string - */ - protected $connection = self::DEFAULT_CONNECTION_NAME; - - /** - * @var \Illuminate\Database\DatabaseManager - */ - protected $database; + protected string $connection = self::DEFAULT_CONNECTION_NAME; /** * DatabaseRepository constructor. - * - * @param \Illuminate\Foundation\Application $application - * @param \Illuminate\Database\DatabaseManager $database */ - public function __construct(Application $application, DatabaseManager $database) + public function __construct(Application $application, private DatabaseManager $database) { parent::__construct($application); - - $this->database = $database; } /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Database::class; } - /** - * Set the connection name to execute statements against. - * - * @param string $connection - * @return $this - */ - public function setConnection(string $connection) - { - $this->connection = $connection; - - return $this; - } - /** * Return the connection to execute statements against. - * - * @return string */ public function getConnection(): string { @@ -69,22 +38,25 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor } /** - * Return all of the databases belonging to a server. - * - * @param int $server - * @return \Illuminate\Support\Collection + * Set the connection name to execute statements against. */ - public function getDatabasesForServer(int $server): Collection + public function setConnection(string $connection): self { - return $this->getBuilder()->where('server_id', $server)->get($this->getColumns()); + $this->connection = $connection; + + return $this; } /** - * Return all of the databases for a given host with the server relationship loaded. - * - * @param int $host - * @param int $count - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + * Return all the databases belonging to a server. + */ + public function getDatabasesForServer(int $server): Collection + { + return $this->getBuilder()->with('host')->where('server_id', $server)->get($this->getColumns()); + } + + /** + * Return all the databases for a given host with the server relationship loaded. */ public function getDatabasesForHost(int $host, int $count = 25): LengthAwarePaginator { @@ -93,36 +65,8 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor ->paginate($count, $this->getColumns()); } - /** - * Create a new database if it does not already exist on the host with - * the provided details. - * - * @param array $data - * @return \Pterodactyl\Models\Database - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException - */ - public function createIfNotExists(array $data): Database - { - $count = $this->getBuilder()->where([ - ['server_id', '=', array_get($data, 'server_id')], - ['database_host_id', '=', array_get($data, 'database_host_id')], - ['database', '=', array_get($data, 'database')], - ])->count(); - - if ($count > 0) { - throw new DuplicateDatabaseNameException('A database with those details already exists for the specified server.'); - } - - return $this->create($data); - } - /** * Create a new database on a given connection. - * - * @param string $database - * @return bool */ public function createDatabase(string $database): bool { @@ -131,29 +75,27 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor /** * Create a new database user on a given connection. - * - * @param string $username - * @param string $remote - * @param string $password - * @return bool */ - public function createUser(string $username, string $remote, string $password): bool + public function createUser(string $username, string $remote, string $password, ?int $max_connections): bool { - return $this->run(sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password)); + $args = [$username, $remote, $password]; + $command = 'CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\''; + + if (!empty($max_connections)) { + $args[] = $max_connections; + $command .= ' WITH MAX_USER_CONNECTIONS %s'; + } + + return $this->run(sprintf($command, ...$args)); } /** * Give a specific user access to a given database. - * - * @param string $database - * @param string $username - * @param string $remote - * @return bool */ public function assignUserToDatabase(string $database, string $username, string $remote): bool { return $this->run(sprintf( - 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, LOCK TABLES, EXECUTE ON `%s`.* TO `%s`@`%s`', + 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, REFERENCES, INDEX, LOCK TABLES, CREATE ROUTINE, ALTER ROUTINE, EXECUTE, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW, EVENT, TRIGGER ON `%s`.* TO `%s`@`%s`', $database, $username, $remote @@ -162,8 +104,6 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor /** * Flush the privileges for a given connection. - * - * @return bool */ public function flush(): bool { @@ -172,9 +112,6 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor /** * Drop a given database on a specific connection. - * - * @param string $database - * @return bool */ public function dropDatabase(string $database): bool { @@ -183,10 +120,6 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor /** * Drop a given user on a specific connection. - * - * @param string $username - * @param string $remote - * @return mixed */ public function dropUser(string $username, string $remote): bool { @@ -195,9 +128,6 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor /** * Run the provided statement against the database on a given connection. - * - * @param string $statement - * @return bool */ private function run(string $statement): bool { diff --git a/app/Repositories/Eloquent/EggRepository.php b/app/Repositories/Eloquent/EggRepository.php index a3a96efb0..8c4a01a27 100644 --- a/app/Repositories/Eloquent/EggRepository.php +++ b/app/Repositories/Eloquent/EggRepository.php @@ -13,10 +13,8 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Egg::class; } @@ -24,24 +22,19 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface /** * Return an egg with the variables relation attached. * - * @param int $id - * @return \Pterodactyl\Models\Egg - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithVariables(int $id): Egg { try { return $this->getBuilder()->with('variables')->findOrFail($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** * Return all eggs and their relations to be used in the daemon API. - * - * @return \Illuminate\Database\Eloquent\Collection */ public function getAllWithCopyAttributes(): Collection { @@ -52,27 +45,22 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface * Return an egg with the scriptFrom and configFrom relations loaded onto the model. * * @param int|string $value - * @param string $column - * @return \Pterodactyl\Models\Egg * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithCopyAttributes($value, string $column = 'id'): Egg { - Assert::true((is_digit($value) || is_string($value)), 'First argument passed to getWithCopyAttributes must be an integer or string, received %s.'); + Assert::true(is_digit($value) || is_string($value), 'First argument passed to getWithCopyAttributes must be an integer or string, received %s.'); try { return $this->getBuilder()->with('scriptFrom', 'configFrom')->where($column, '=', $value)->firstOrFail($this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** - * Return all of the data needed to export a service. - * - * @param int $id - * @return \Pterodactyl\Models\Egg + * Return all the data needed to export a service. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -80,17 +68,13 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface { try { return $this->getBuilder()->with('scriptFrom', 'configFrom', 'variables')->findOrFail($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** * Confirm a copy script belongs to the same nest as the item trying to use it. - * - * @param int $copyFromId - * @param int $service - * @return bool */ public function isCopyableScript(int $copyFromId, int $service): bool { diff --git a/app/Repositories/Eloquent/EggVariableRepository.php b/app/Repositories/Eloquent/EggVariableRepository.php index 9d84b9db1..8ca35debb 100644 --- a/app/Repositories/Eloquent/EggVariableRepository.php +++ b/app/Repositories/Eloquent/EggVariableRepository.php @@ -10,10 +10,8 @@ class EggVariableRepository extends EloquentRepository implements EggVariableRep { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return EggVariable::class; } @@ -21,9 +19,6 @@ class EggVariableRepository extends EloquentRepository implements EggVariableRep /** * Return editable variables for a given egg. Editable variables must be set to * user viewable in order to be picked up by this function. - * - * @param int $egg - * @return \Illuminate\Support\Collection */ public function getEditableVariables(int $egg): Collection { diff --git a/app/Repositories/Eloquent/EloquentRepository.php b/app/Repositories/Eloquent/EloquentRepository.php index cf0931799..688fb34cb 100644 --- a/app/Repositories/Eloquent/EloquentRepository.php +++ b/app/Repositories/Eloquent/EloquentRepository.php @@ -2,36 +2,69 @@ namespace Pterodactyl\Repositories\Eloquent; +use Illuminate\Http\Request; use Webmozart\Assert\Assert; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; +use Illuminate\Database\Eloquent\Model; use Pterodactyl\Repositories\Repository; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\Expression; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\RepositoryInterface; use Pterodactyl\Exceptions\Model\DataValidationException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; abstract class EloquentRepository extends Repository implements RepositoryInterface { + protected bool $useRequestFilters = false; + + /** + * Determines if the repository function should use filters off the request object + * present when returning results. This allows repository methods to be called in API + * context's such that we can pass through ?filter[name]=Dane&sort=desc for example. + */ + public function usingRequestFilters(bool $usingFilters = true): self + { + $this->useRequestFilters = $usingFilters; + + return $this; + } + + /** + * Returns the request instance. + */ + protected function request(): Request + { + return $this->app->make(Request::class); + } + + /** + * Paginate the response data based on the page para. + */ + protected function paginate(Builder $instance, int $default = 50): LengthAwarePaginator + { + if (!$this->useRequestFilters) { + return $instance->paginate($default); + } + + return $instance->paginate($this->request()->query('per_page', $default)); + } + /** * Return an instance of the eloquent model bound to this * repository instance. - * - * @return \Illuminate\Database\Eloquent\Model */ - public function getModel() + public function getModel(): Model { return $this->model; } /** * Return an instance of the builder to use for this repository. - * - * @return \Illuminate\Database\Eloquent\Builder */ - public function getBuilder() + public function getBuilder(): Builder { return $this->getModel()->newQuery(); } @@ -39,23 +72,19 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Create a new record in the database and return the associated model. * - * @param array $fields - * @param bool $validate - * @param bool $force - * @return \Illuminate\Database\Eloquent\Model|bool - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function create(array $fields, bool $validate = true, bool $force = false) + public function create(array $fields, bool $validate = true, bool $force = false): Model|bool { + /** @var \Pterodactyl\Models\Model $instance */ $instance = $this->getBuilder()->newModelInstance(); ($force) ? $instance->forceFill($fields) : $instance->fill($fields); - if (! $validate) { + if (!$validate) { $saved = $instance->skipValidation()->save(); } else { - if (! $saved = $instance->save()) { - throw new DataValidationException($instance->getValidator()); + if (!$saved = $instance->save()) { + throw new DataValidationException($instance->getValidator(), $instance); } } @@ -65,25 +94,19 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Find a model that has the specific ID passed. * - * @param int $id - * @return \Illuminate\Database\Eloquent\Model - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function find(int $id) + public function find(int $id): Model { try { return $this->getBuilder()->findOrFail($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** * Find a model matching an array of where clauses. - * - * @param array $fields - * @return \Illuminate\Support\Collection */ public function findWhere(array $fields): Collection { @@ -93,25 +116,19 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Find and return the first matching instance for the given fields. * - * @param array $fields - * @return \Illuminate\Database\Eloquent\Model - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function findFirstWhere(array $fields) + public function findFirstWhere(array $fields): Model { try { return $this->getBuilder()->where($fields)->firstOrFail($this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** * Return a count of records matching the passed arguments. - * - * @param array $fields - * @return int */ public function findCountWhere(array $fields): int { @@ -120,10 +137,6 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Delete a given record from the database. - * - * @param int $id - * @param bool $destroy - * @return int */ public function delete(int $id, bool $destroy = false): int { @@ -132,10 +145,6 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Delete records matching the given attributes. - * - * @param array $attributes - * @param bool $force - * @return int */ public function deleteWhere(array $attributes, bool $force = false): int { @@ -147,44 +156,42 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Update a given ID with the passed array of fields. * - * @param int $id - * @param array $fields - * @param bool $validate - * @param bool $force - * @return \Illuminate\Database\Eloquent\Model|bool - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update($id, array $fields, bool $validate = true, bool $force = false) + public function update(int $id, array $fields, bool $validate = true, bool $force = false): Model|bool { try { + /** @var \Pterodactyl\Models\Model $instance */ $instance = $this->getBuilder()->where('id', $id)->firstOrFail(); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } ($force) ? $instance->forceFill($fields) : $instance->fill($fields); - if (! $validate) { + if (!$validate) { $saved = $instance->skipValidation()->save(); } else { - if (! $saved = $instance->save()) { - throw new DataValidationException($instance->getValidator()); + if (!$saved = $instance->save()) { + throw new DataValidationException($instance->getValidator(), $instance); } } return ($this->withFresh) ? $instance->fresh() : $saved; } + /** + * Update a model using the attributes passed. + */ + public function updateWhere(array $attributes, array $values): int + { + return $this->getBuilder()->where($attributes)->update($values); + } + /** * Perform a mass update where matching records are updated using whereIn. * This does not perform any model data validation. - * - * @param string $column - * @param array $values - * @param array $fields - * @return int */ public function updateWhereIn(string $column, array $values, array $fields): int { @@ -196,16 +203,10 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Update a record if it exists in the database, otherwise create it. * - * @param array $where - * @param array $fields - * @param bool $validate - * @param bool $force - * @return \Illuminate\Database\Eloquent\Model - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false) + public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false): Model|bool { foreach ($where as $item) { Assert::true(is_scalar($item) || is_null($item), 'First argument passed to updateOrCreate should be an array of scalar or null values, received an array value of %s.'); @@ -213,7 +214,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf try { $instance = $this->setColumns('id')->findFirstWhere($where); - } catch (RecordNotFoundException $exception) { + } catch (RecordNotFoundException) { return $this->create(array_merge($where, $fields), $validate, $force); } @@ -223,40 +224,24 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Return all records associated with the given model. * - * @return \Illuminate\Support\Collection + * @deprecated Just use the model */ public function all(): Collection { - $instance = $this->getBuilder(); - if (is_subclass_of(get_called_class(), SearchableInterface::class) && $this->hasSearchTerm()) { - $instance = $instance->search($this->getSearchTerm()); - } - - return $instance->get($this->getColumns()); + return $this->getBuilder()->get($this->getColumns()); } /** * Return a paginated result set using a search term if set on the repository. - * - * @param int $perPage - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ public function paginated(int $perPage): LengthAwarePaginator { - $instance = $this->getBuilder(); - if (is_subclass_of(get_called_class(), SearchableInterface::class) && $this->hasSearchTerm()) { - $instance = $instance->search($this->getSearchTerm()); - } - - return $instance->paginate($perPage, $this->getColumns()); + return $this->getBuilder()->paginate($perPage, $this->getColumns()); } /** * Insert a single or multiple records into the database at once skipping * validation and mass assignment checking. - * - * @param array $data - * @return bool */ public function insert(array $data): bool { @@ -265,9 +250,6 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Insert multiple records into the database and ignore duplicates. - * - * @param array $values - * @return bool */ public function insertIgnore(array $values): bool { @@ -281,7 +263,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf } $bindings = array_values(array_filter(array_flatten($values, 1), function ($binding) { - return ! $binding instanceof Expression; + return !$binding instanceof Expression; })); $grammar = $this->getBuilder()->toBase()->getGrammar(); @@ -292,7 +274,17 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf return sprintf('(%s)', $grammar->parameterize($record)); })->implode(', '); - $statement = "insert ignore into $table ($columns) values $parameters"; + $driver = DB::getPdo()->getAttribute(\PDO::ATTR_DRIVER_NAME); + switch ($driver) { + case 'mysql': + $statement = "insert ignore into $table ($columns) values $parameters"; + break; + case 'pgsql': + $statement = "insert into $table ($columns) values $parameters on conflict do nothing"; + break; + default: + throw new \RuntimeException("Unsupported database driver \"$driver\" for insert ignore."); + } return $this->getBuilder()->getConnection()->statement($statement, $bindings); } @@ -300,7 +292,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf /** * Get the amount of entries in the database. * - * @return int + * @deprecated just use the count method off a model */ public function count(): int { diff --git a/app/Repositories/Eloquent/LocationRepository.php b/app/Repositories/Eloquent/LocationRepository.php index 47d5e321a..e25737cb3 100644 --- a/app/Repositories/Eloquent/LocationRepository.php +++ b/app/Repositories/Eloquent/LocationRepository.php @@ -4,29 +4,22 @@ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Location; use Illuminate\Support\Collection; -use Pterodactyl\Repositories\Concerns\Searchable; use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; class LocationRepository extends EloquentRepository implements LocationRepositoryInterface { - use Searchable; - /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Location::class; } /** * Return locations with a count of nodes and servers attached to it. - * - * @return \Illuminate\Support\Collection */ public function getAllWithDetails(): Collection { @@ -34,9 +27,7 @@ class LocationRepository extends EloquentRepository implements LocationRepositor } /** - * Return all of the available locations with the nodes as a relationship. - * - * @return \Illuminate\Support\Collection + * Return all the available locations with the nodes as a relationship. */ public function getAllWithNodes(): Collection { @@ -44,10 +35,7 @@ class LocationRepository extends EloquentRepository implements LocationRepositor } /** - * Return all of the nodes and their respective count of servers for a location. - * - * @param int $id - * @return mixed + * Return all the nodes and their respective count of servers for a location. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -55,25 +43,22 @@ class LocationRepository extends EloquentRepository implements LocationRepositor { try { return $this->getBuilder()->with('nodes.servers')->findOrFail($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** * Return a location and the count of nodes in that location. * - * @param int $id - * @return mixed - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithNodeCount(int $id): Location { try { return $this->getBuilder()->withCount('nodes')->findOrFail($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } } diff --git a/app/Repositories/Eloquent/MountRepository.php b/app/Repositories/Eloquent/MountRepository.php new file mode 100644 index 000000000..b33e6cb24 --- /dev/null +++ b/app/Repositories/Eloquent/MountRepository.php @@ -0,0 +1,57 @@ +getBuilder()->withCount('eggs', 'nodes')->get($this->getColumns()); + } + + /** + * Return all the mounts and their respective relations. + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithRelations(string $id): Mount + { + try { + return $this->getBuilder()->with('eggs', 'nodes')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException(); + } + } + + /** + * Return mounts available to a server (ignoring if they are or are not mounted). + */ + public function getMountListForServer(Server $server): Collection + { + return $this->getBuilder() + ->whereHas('eggs', function ($q) use ($server) { + $q->where('id', '=', $server->egg_id); + }) + ->whereHas('nodes', function ($q) use ($server) { + $q->where('id', '=', $server->node_id); + }) + ->get($this->getColumns()); + } +} diff --git a/app/Repositories/Eloquent/NestRepository.php b/app/Repositories/Eloquent/NestRepository.php index 9c0fcf73c..f472e4b80 100644 --- a/app/Repositories/Eloquent/NestRepository.php +++ b/app/Repositories/Eloquent/NestRepository.php @@ -1,15 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Nest; +use Illuminate\Database\Eloquent\Collection; use Pterodactyl\Contracts\Repository\NestRepositoryInterface; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; @@ -17,30 +11,25 @@ class NestRepository extends EloquentRepository implements NestRepositoryInterfa { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Nest::class; } /** - * Return a nest or all nests with their associated eggs, variables, and packs. - * - * @param int $id - * @return \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Nest + * Return a nest or all nests with their associated eggs and variables. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithEggs(int $id = null) + public function getWithEggs(int $id = null): Collection|Nest { - $instance = $this->getBuilder()->with('eggs.packs', 'eggs.variables'); + $instance = $this->getBuilder()->with('eggs', 'eggs.variables'); - if (! is_null($id)) { + if (!is_null($id)) { $instance = $instance->find($id, $this->getColumns()); - if (! $instance) { - throw new RecordNotFoundException; + if (!$instance) { + throw new RecordNotFoundException(); } return $instance; @@ -50,21 +39,18 @@ class NestRepository extends EloquentRepository implements NestRepositoryInterfa } /** - * Return a nest or all nests and the count of eggs, packs, and servers for that nest. - * - * @param int|null $id - * @return \Pterodactyl\Models\Nest|\Illuminate\Database\Eloquent\Collection + * Return a nest or all nests and the count of eggs and servers for that nest. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithCounts(int $id = null) + public function getWithCounts(int $id = null): Collection|Nest { - $instance = $this->getBuilder()->withCount(['eggs', 'packs', 'servers']); + $instance = $this->getBuilder()->withCount(['eggs', 'servers']); - if (! is_null($id)) { + if (!is_null($id)) { $instance = $instance->find($id, $this->getColumns()); - if (! $instance) { - throw new RecordNotFoundException; + if (!$instance) { + throw new RecordNotFoundException(); } return $instance; @@ -76,16 +62,13 @@ class NestRepository extends EloquentRepository implements NestRepositoryInterfa /** * Return a nest along with its associated eggs and the servers relation on those eggs. * - * @param int $id - * @return \Pterodactyl\Models\Nest - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithEggServers(int $id): Nest { $instance = $this->getBuilder()->with('eggs.servers')->find($id, $this->getColumns()); - if (! $instance) { - throw new RecordNotFoundException; + if (!$instance) { + throw new RecordNotFoundException(); } /* @var Nest $instance */ diff --git a/app/Repositories/Eloquent/NodeRepository.php b/app/Repositories/Eloquent/NodeRepository.php index 3b86f2ee1..0ee703a79 100644 --- a/app/Repositories/Eloquent/NodeRepository.php +++ b/app/Repositories/Eloquent/NodeRepository.php @@ -2,69 +2,54 @@ namespace Pterodactyl\Repositories\Eloquent; -use Generator; use Pterodactyl\Models\Node; use Illuminate\Support\Collection; -use Pterodactyl\Repositories\Concerns\Searchable; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; class NodeRepository extends EloquentRepository implements NodeRepositoryInterface { - use Searchable; - /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Node::class; } /** * Return the usage stats for a single node. - * - * @param \Pterodactyl\Models\Node $node - * @return array */ public function getUsageStats(Node $node): array { - $stats = $this->getBuilder()->select( - $this->getBuilder()->raw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') - )->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first(); + $stats = $node->loadServerSums(); - return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) { - $maxUsage = $node->{$key}; - if ($node->{$key . '_overallocate'} > 0) { - $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); - } + return Collection::make(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory]) + ->mapWithKeys(function ($value, $key) use ($node) { + $maxUsage = $node->{$key}; + if ($node->{$key . '_overallocate'} > 0) { + $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); + } - $percent = ($value / $maxUsage) * 100; + $percent = ($value / $maxUsage) * 100; - return [ - $key => [ - 'value' => number_format($value), - 'max' => number_format($maxUsage), - 'percent' => $percent, - 'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'), - ], - ]; - })->toArray(); + return [ + $key => [ + 'value' => number_format($value), + 'max' => number_format($maxUsage), + 'percent' => $percent, + 'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'), + ], + ]; + }) + ->toArray(); } /** * Return the usage stats for a single node. - * - * @param \Pterodactyl\Models\Node $node - * @return array */ public function getUsageStatsRaw(Node $node): array { - $stats = $this->getBuilder()->select( - $this->getBuilder()->raw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') - )->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first(); + $stats = $node->loadServerSums(); return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) { $maxUsage = $node->{$key}; @@ -81,41 +66,19 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa })->toArray(); } - /** - * Return all available nodes with a searchable interface. - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getNodeListingData(): LengthAwarePaginator - { - $instance = $this->getBuilder()->with('location')->withCount('servers'); - - if ($this->hasSearchTerm()) { - $instance->search($this->getSearchTerm()); - } - - return $instance->paginate(25, $this->getColumns()); - } - /** * Return a single node with location and server information. - * - * @param \Pterodactyl\Models\Node $node - * @param bool $refresh - * @return \Pterodactyl\Models\Node */ public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node { - if (! $node->relationLoaded('location') || $refresh) { + if (!$node->relationLoaded('location') || $refresh) { $node->load('location'); } // This is quite ugly and can probably be improved down the road. // And by probably, I mean it should. if (is_null($node->servers_count) || $refresh) { - $node->load('servers'); - $node->setRelation('servers_count', count($node->getRelation('servers'))); - unset($node->servers); + $node->loadCount('servers'); } return $node; @@ -124,15 +87,17 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa /** * Attach a paginated set of allocations to a node mode including * any servers that are also attached to those allocations. - * - * @param \Pterodactyl\Models\Node $node - * @param bool $refresh - * @return \Pterodactyl\Models\Node */ public function loadNodeAllocations(Node $node, bool $refresh = false): Node { - $node->setRelation('allocations', - $node->allocations()->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')->orderByRaw('INET_ATON(ip) ASC')->orderBy('port', 'asc')->with('server:id,name')->paginate(50) + $node->setRelation( + 'allocations', + $node->allocations() + ->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL') + ->orderByRaw('INET_ATON(ip) ASC') + ->orderBy('port') + ->with('server:id,name') + ->paginate(50) ); return $node; @@ -140,8 +105,6 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa /** * Return a collection of nodes for all locations to use in server creation UI. - * - * @return \Illuminate\Support\Collection */ public function getNodesForServerCreation(): Collection { @@ -164,28 +127,4 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa ]; })->values(); } - - /** - * Return the IDs of all nodes that exist in the provided locations and have the space - * available to support the additional disk and memory provided. - * - * @param array $locations - * @param int $disk - * @param int $memory - * @return \Generator - */ - public function getNodesWithResourceUse(array $locations, int $disk, int $memory): Generator - { - $instance = $this->getBuilder() - ->select(['nodes.id', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate']) - ->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') - ->leftJoin('servers', 'servers.node_id', '=', 'nodes.id') - ->where('nodes.public', 1); - - if (! empty($locations)) { - $instance->whereIn('nodes.location_id', $locations); - } - - return $instance->groupBy('nodes.id')->cursor(); - } } diff --git a/app/Repositories/Eloquent/PackRepository.php b/app/Repositories/Eloquent/PackRepository.php deleted file mode 100644 index 922e6415a..000000000 --- a/app/Repositories/Eloquent/PackRepository.php +++ /dev/null @@ -1,53 +0,0 @@ -load(['servers.node', 'servers.user']); - } - - $pack->loadMissing(['servers.node', 'servers.user']); - - return $pack; - } - - /** - * Return a paginated listing of packs with their associated egg and server count. - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function paginateWithEggAndServerCount(): LengthAwarePaginator - { - return $this->getBuilder()->with('egg')->withCount('servers') - ->search($this->getSearchTerm()) - ->paginate(50, $this->getColumns()); - } -} diff --git a/app/Repositories/Eloquent/PermissionRepository.php b/app/Repositories/Eloquent/PermissionRepository.php index ad2fa6386..603829bc1 100644 --- a/app/Repositories/Eloquent/PermissionRepository.php +++ b/app/Repositories/Eloquent/PermissionRepository.php @@ -2,7 +2,6 @@ namespace Pterodactyl\Repositories\Eloquent; -use Pterodactyl\Models\Permission; use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; class PermissionRepository extends EloquentRepository implements PermissionRepositoryInterface @@ -10,10 +9,10 @@ class PermissionRepository extends EloquentRepository implements PermissionRepos /** * Return the model backing this repository. * - * @return string + * @throws \Exception */ - public function model() + public function model(): string { - return Permission::class; + throw new \Exception('This functionality is not implemented.'); } } diff --git a/app/Repositories/Eloquent/RecoveryTokenRepository.php b/app/Repositories/Eloquent/RecoveryTokenRepository.php new file mode 100644 index 000000000..5d4dc4850 --- /dev/null +++ b/app/Repositories/Eloquent/RecoveryTokenRepository.php @@ -0,0 +1,13 @@ +relationLoaded('tasks') || $refresh) { - $schedule->load('tasks'); - } - - return $schedule; - } - - /** - * Return a schedule model with all of the associated tasks as a relationship. - * - * @param int $schedule - * @return \Pterodactyl\Models\Schedule + * Return a schedule model with all the associated tasks as a relationship. * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -60,23 +35,8 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor { try { return $this->getBuilder()->with('tasks')->findOrFail($schedule, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } - - /** - * Return all of the schedules that should be processed. - * - * @param string $timestamp - * @return \Illuminate\Support\Collection - */ - public function getSchedulesToProcess(string $timestamp): Collection - { - return $this->getBuilder()->with('tasks') - ->where('is_active', true) - ->where('is_processing', false) - ->where('next_run_at', '<=', $timestamp) - ->get($this->getColumns()); - } } diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index 820093cf4..a353dba46 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -2,12 +2,9 @@ namespace Pterodactyl\Repositories\Eloquent; -use Pterodactyl\Models\Node; -use Pterodactyl\Models\User; -use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Illuminate\Support\Collection; -use Pterodactyl\Repositories\Concerns\Searchable; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; @@ -15,41 +12,20 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class ServerRepository extends EloquentRepository implements ServerRepositoryInterface { - use Searchable; - /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Server::class; } - /** - * Returns a listing of all servers that exist including relationships. - * - * @param int $paginate - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getAllServers(int $paginate): LengthAwarePaginator - { - $instance = $this->getBuilder()->with('node', 'user', 'allocation')->search($this->getSearchTerm()); - - return $instance->paginate($paginate, $this->getColumns()); - } - /** * Load the egg relations onto the server model. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function loadEggRelations(Server $server, bool $refresh = false): Server { - if (! $server->relationLoaded('egg') || $refresh) { + if (!$server->relationLoaded('egg') || $refresh) { $server->load('egg.scriptFrom'); } @@ -58,18 +34,30 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt /** * Return a collection of servers with their associated data for rebuild operations. - * - * @param int|null $server - * @param int|null $node - * @return \Illuminate\Support\Collection */ public function getDataForRebuild(int $server = null, int $node = null): Collection { - $instance = $this->getBuilder()->with(['allocation', 'allocations', 'pack', 'egg', 'node']); + $instance = $this->getBuilder()->with(['allocation', 'allocations', 'egg', 'node']); - if (! is_null($server) && is_null($node)) { + if (!is_null($server) && is_null($node)) { $instance = $instance->where('id', '=', $server); - } elseif (is_null($server) && ! is_null($node)) { + } elseif (is_null($server) && !is_null($node)) { + $instance = $instance->where('node_id', '=', $node); + } + + return $instance->get($this->getColumns()); + } + + /** + * Return a collection of servers with their associated data for reinstall operations. + */ + public function getDataForReinstall(int $server = null, int $node = null): Collection + { + $instance = $this->getBuilder()->with(['allocation', 'allocations', 'egg', 'node']); + + if (!is_null($server) && is_null($node)) { + $instance = $instance->where('id', '=', $server); + } elseif (is_null($server) && !is_null($node)) { $instance = $instance->where('node_id', '=', $node); } @@ -79,9 +67,6 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt /** * Return a server model and all variables associated with the server. * - * @param int $id - * @return \Pterodactyl\Models\Server - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function findWithVariables(int $id): Server @@ -90,8 +75,8 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt return $this->getBuilder()->with('egg.variables', 'variables') ->where($this->getModel()->getKeyName(), '=', $id) ->firstOrFail($this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } @@ -99,66 +84,23 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt * Get the primary allocation for a given server. If a model is passed into * the function, load the allocation relationship onto it. Otherwise, find and * return the server from the database. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function getPrimaryAllocation(Server $server, bool $refresh = false): Server { - if (! $server->relationLoaded('allocation') || $refresh) { + if (!$server->relationLoaded('allocation') || $refresh) { $server->load('allocation'); } return $server; } - /** - * Return all of the server variables possible and default to the variable - * default if there is no value defined for the specific server requested. - * - * @param int $id - * @param bool $returnAsObject - * @return array|object - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getVariablesWithValues(int $id, bool $returnAsObject = false) - { - try { - $instance = $this->getBuilder()->with('variables', 'egg.variables')->find($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; - } - - $data = []; - $instance->getRelation('egg')->getRelation('variables')->each(function ($item) use (&$data, $instance) { - $display = $instance->getRelation('variables')->where('variable_id', $item->id)->pluck('variable_value')->first(); - - $data[$item->env_variable] = $display ?? $item->default_value; - }); - - if ($returnAsObject) { - return (object) [ - 'data' => $data, - 'server' => $instance, - ]; - } - - return $data; - } - /** * Return enough data to be used for the creation of a server via the daemon. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function getDataForCreation(Server $server, bool $refresh = false): Server { - foreach (['allocation', 'allocations', 'pack', 'egg'] as $relation) { - if (! $server->relationLoaded($relation) || $refresh) { + foreach (['allocation', 'allocations', 'egg'] as $relation) { + if (!$server->relationLoaded($relation) || $refresh) { $server->load($relation); } } @@ -168,14 +110,10 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt /** * Load associated databases onto the server model. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server */ public function loadDatabaseRelations(Server $server, bool $refresh = false): Server { - if (! $server->relationLoaded('databases') || $refresh) { + if (!$server->relationLoaded('databases') || $refresh) { $server->load('databases.host'); } @@ -184,169 +122,52 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt /** * Get data for use when updating a server on the Daemon. Returns an array of - * the egg and pack UUID which are used for build and rebuild. Only loads relations + * the egg which is used for build and rebuild. Only loads relations * if they are missing, or refresh is set to true. - * - * @param \Pterodactyl\Models\Server $server - * @param bool $refresh - * @return array */ public function getDaemonServiceData(Server $server, bool $refresh = false): array { - if (! $server->relationLoaded('egg') || $refresh) { + if (!$server->relationLoaded('egg') || $refresh) { $server->load('egg'); } - if (! $server->relationLoaded('pack') || $refresh) { - $server->load('pack'); - } - return [ 'egg' => $server->getRelation('egg')->uuid, - 'pack' => is_null($server->getRelation('pack')) ? null : $server->getRelation('pack')->uuid, ]; } - /** - * Return a paginated list of servers that a user can access at a given level. - * - * @param \Pterodactyl\Models\User $user - * @param int $level - * @param bool|int $paginate - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection - */ - public function filterUserAccessServers(User $user, int $level, $paginate = 25) - { - $instance = $this->getBuilder()->select($this->getColumns())->with(['user', 'node', 'allocation']); - - // If access level is set to owner, only display servers - // that the user owns. - if ($level === User::FILTER_LEVEL_OWNER) { - $instance->where('owner_id', $user->id); - } - - // If set to all, display all servers they can access, including - // those they access as an admin. If set to subuser, only return - // the servers they can access because they are owner, or marked - // as a subuser of the server. - elseif (($level === User::FILTER_LEVEL_ALL && ! $user->root_admin) || $level === User::FILTER_LEVEL_SUBUSER) { - $instance->whereIn('id', $this->getUserAccessServers($user->id)); - } - - // If set to admin, only display the servers a user can access - // as an administrator (leaves out owned and subuser of). - elseif ($level === User::FILTER_LEVEL_ADMIN && $user->root_admin) { - $instance->whereNotIn('id', $this->getUserAccessServers($user->id)); - } - - $instance->search($this->getSearchTerm()); - - return $paginate ? $instance->paginate($paginate) : $instance->get(); - } - /** * Return a server by UUID. * - * @param string $uuid - * @return \Pterodactyl\Models\Server - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getByUuid(string $uuid): Server { - Assert::notEmpty($uuid, 'Expected non-empty string as first argument passed to ' . __METHOD__); - try { - return $this->getBuilder()->with('nest', 'node')->where(function ($query) use ($uuid) { - $query->where('uuidShort', $uuid)->orWhere('uuid', $uuid); - })->firstOrFail($this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + /** @var \Pterodactyl\Models\Server $model */ + $model = $this->getBuilder() + ->with('nest', 'node') + ->where(function (Builder $query) use ($uuid) { + $query->where('uuidShort', $uuid)->orWhere('uuid', $uuid); + }) + ->firstOrFail($this->getColumns()); + + return $model; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } - /** - * Return all of the servers that should have a power action performed against them. - * - * @param int[] $servers - * @param int[] $nodes - * @param bool $returnCount - * @return int|\Generator - */ - public function getServersForPowerAction(array $servers = [], array $nodes = [], bool $returnCount = false) - { - $instance = $this->getBuilder(); - - if (! empty($nodes) && ! empty($servers)) { - $instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes); - } elseif (empty($nodes) && ! empty($servers)) { - $instance->whereIn('id', $servers); - } elseif (! empty($nodes) && empty($servers)) { - $instance->whereIn('node_id', $nodes); - } - - if ($returnCount) { - return $instance->count(); - } - - return $instance->with('node')->cursor(); - } - - /** - * Return the total number of servers that will be affected by the query. - * - * @param int[] $servers - * @param int[] $nodes - * @return int - */ - public function getServersForPowerActionCount(array $servers = [], array $nodes = []): int - { - return $this->getServersForPowerAction($servers, $nodes, true); - } - /** * Check if a given UUID and UUID-Short string are unique to a server. - * - * @param string $uuid - * @param string $short - * @return bool */ public function isUniqueUuidCombo(string $uuid, string $short): bool { - return ! $this->getBuilder()->where('uuid', '=', $uuid)->orWhere('uuidShort', '=', $short)->exists(); + return !$this->getBuilder()->where('uuid', '=', $uuid)->orWhere('uuidShort', '=', $short)->exists(); } /** - * Return an array of server IDs that a given user can access based - * on owner and subuser permissions. - * - * @param int $user - * @return int[] - */ - private function getUserAccessServers(int $user): array - { - return $this->getBuilder()->select('id')->where('owner_id', $user)->union( - $this->app->make(SubuserRepository::class)->getBuilder()->select('server_id')->where('user_id', $user) - )->pluck('id')->all(); - } - - /** - * Get the amount of servers that are suspended. - * - * @return int - */ - public function getSuspendedServersCount(): int - { - return $this->getBuilder()->where('suspended', true)->count(); - } - - /** - * Returns all of the servers that exist for a given node in a paginated response. - * - * @param int $node - * @param int $limit - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + * Returns all the servers that exist for a given node in a paginated response. */ public function loadAllServersForNode(int $node, int $limit): LengthAwarePaginator { diff --git a/app/Repositories/Eloquent/ServerVariableRepository.php b/app/Repositories/Eloquent/ServerVariableRepository.php index d0d5e4dba..26ded2cf6 100644 --- a/app/Repositories/Eloquent/ServerVariableRepository.php +++ b/app/Repositories/Eloquent/ServerVariableRepository.php @@ -9,10 +9,8 @@ class ServerVariableRepository extends EloquentRepository implements ServerVaria { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return ServerVariable::class; } diff --git a/app/Repositories/Eloquent/SessionRepository.php b/app/Repositories/Eloquent/SessionRepository.php index a49226452..5aa355dc0 100644 --- a/app/Repositories/Eloquent/SessionRepository.php +++ b/app/Repositories/Eloquent/SessionRepository.php @@ -10,19 +10,14 @@ class SessionRepository extends EloquentRepository implements SessionRepositoryI { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Session::class; } /** - * Return all of the active sessions for a user. - * - * @param int $user - * @return \Illuminate\Support\Collection + * Return all the active sessions for a user. */ public function getUserSessions(int $user): Collection { @@ -31,12 +26,8 @@ class SessionRepository extends EloquentRepository implements SessionRepositoryI /** * Delete a session for a given user. - * - * @param int $user - * @param string $session - * @return null|int */ - public function deleteUserSession(int $user, string $session) + public function deleteUserSession(int $user, string $session): ?int { return $this->getBuilder()->where('user_id', $user)->where('id', $session)->delete(); } diff --git a/app/Repositories/Eloquent/SettingsRepository.php b/app/Repositories/Eloquent/SettingsRepository.php index 0d25a1b80..e8003129a 100644 --- a/app/Repositories/Eloquent/SettingsRepository.php +++ b/app/Repositories/Eloquent/SettingsRepository.php @@ -7,22 +7,14 @@ use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface; class SettingsRepository extends EloquentRepository implements SettingsRepositoryInterface { - /** - * @var array - */ - private static $cache = []; + private static array $cache = []; - /** - * @var array - */ - private static $databaseMiss = []; + private static array $databaseMiss = []; /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Setting::class; } @@ -30,11 +22,7 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor /** * Store a new persistent setting in the database. * - * @param string $key - * @param string|null $value - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function set(string $key, string $value = null) { @@ -47,12 +35,8 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor /** * Retrieve a persistent setting from the database. - * - * @param string $key - * @param mixed $default - * @return mixed */ - public function get(string $key, $default = null) + public function get(string $key, mixed $default = null): mixed { // If item has already been requested return it from the cache. If // we already know it is missing, immediately return the default value. @@ -62,6 +46,7 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor return value($default); } + /** @var Setting $instance */ $instance = $this->getBuilder()->where('key', $key)->first(); if (is_null($instance)) { self::$databaseMiss[$key] = true; @@ -74,8 +59,6 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor /** * Remove a key from the database cache. - * - * @param string $key */ public function forget(string $key) { @@ -85,8 +68,6 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor /** * Remove a key from the cache. - * - * @param string $key */ private function clearCache(string $key) { diff --git a/app/Repositories/Eloquent/SubuserRepository.php b/app/Repositories/Eloquent/SubuserRepository.php index 0296e0dbd..141378753 100644 --- a/app/Repositories/Eloquent/SubuserRepository.php +++ b/app/Repositories/Eloquent/SubuserRepository.php @@ -10,28 +10,22 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Subuser::class; } /** * Return a subuser with the associated server relationship. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @param bool $refresh - * @return \Pterodactyl\Models\Subuser */ public function loadServerAndUserRelations(Subuser $subuser, bool $refresh = false): Subuser { - if (! $subuser->relationLoaded('server') || $refresh) { + if (!$subuser->relationLoaded('server') || $refresh) { $subuser->load('server'); } - if (! $subuser->relationLoaded('user') || $refresh) { + if (!$subuser->relationLoaded('user') || $refresh) { $subuser->load('user'); } @@ -40,18 +34,14 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI /** * Return a subuser with the associated permissions relationship. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @param bool $refresh - * @return \Pterodactyl\Models\Subuser */ public function getWithPermissions(Subuser $subuser, bool $refresh = false): Subuser { - if (! $subuser->relationLoaded('permissions') || $refresh) { + if (!$subuser->relationLoaded('permissions') || $refresh) { $subuser->load('permissions'); } - if (! $subuser->relationLoaded('user') || $refresh) { + if (!$subuser->relationLoaded('user') || $refresh) { $subuser->load('user'); } @@ -61,10 +51,6 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI /** * Return a subuser and associated permissions given a user_id and server_id. * - * @param int $user - * @param int $server - * @return \Pterodactyl\Models\Subuser - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithPermissionsUsingUserAndServer(int $user, int $server): Subuser @@ -75,7 +61,7 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI ])->first(); if (is_null($instance)) { - throw new RecordNotFoundException; + throw new RecordNotFoundException(); } return $instance; diff --git a/app/Repositories/Eloquent/TaskRepository.php b/app/Repositories/Eloquent/TaskRepository.php index 0c1202f59..942c54d47 100644 --- a/app/Repositories/Eloquent/TaskRepository.php +++ b/app/Repositories/Eloquent/TaskRepository.php @@ -11,10 +11,8 @@ class TaskRepository extends EloquentRepository implements TaskRepositoryInterfa { /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return Task::class; } @@ -22,31 +20,25 @@ class TaskRepository extends EloquentRepository implements TaskRepositoryInterfa /** * Get a task and the server relationship for that task. * - * @param int $id - * @return \Pterodactyl\Models\Task - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getTaskForJobProcess(int $id): Task { try { return $this->getBuilder()->with('server.user', 'schedule')->findOrFail($id, $this->getColumns()); - } catch (ModelNotFoundException $exception) { - throw new RecordNotFoundException; + } catch (ModelNotFoundException) { + throw new RecordNotFoundException(); } } /** * Returns the next task in a schedule. - * - * @param int $schedule - * @param int $index - * @return null|\Pterodactyl\Models\Task */ - public function getNextTask(int $schedule, int $index) + public function getNextTask(int $schedule, int $index): ?Task { return $this->getBuilder()->where('schedule_id', '=', $schedule) - ->where('sequence_id', '=', $index + 1) + ->orderBy('sequence_id') + ->where('sequence_id', '>', $index) ->first($this->getColumns()); } } diff --git a/app/Repositories/Eloquent/UserRepository.php b/app/Repositories/Eloquent/UserRepository.php index b69df198c..444534625 100644 --- a/app/Repositories/Eloquent/UserRepository.php +++ b/app/Repositories/Eloquent/UserRepository.php @@ -3,55 +3,15 @@ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\User; -use Illuminate\Support\Collection; -use Pterodactyl\Repositories\Concerns\Searchable; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; class UserRepository extends EloquentRepository implements UserRepositoryInterface { - use Searchable; - /** * Return the model backing this repository. - * - * @return string */ - public function model() + public function model(): string { return User::class; } - - /** - * Return all users with counts of servers and subusers of servers. - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function getAllUsersWithCounts(): LengthAwarePaginator - { - return $this->getBuilder()->withCount('servers', 'subuserOf') - ->search($this->getSearchTerm()) - ->paginate(50, $this->getColumns()); - } - - /** - * Return all matching models for a user in a format that can be used for dropdowns. - * - * @param string|null $query - * @return \Illuminate\Support\Collection - */ - public function filterUsersByQuery(?string $query): Collection - { - $this->setColumns([ - 'id', 'email', 'username', 'name_first', 'name_last', - ]); - - $instance = $this->getBuilder()->search($query)->get($this->getColumns()); - - return $instance->transform(function ($item) { - $item->md5 = md5(strtolower($item->email)); - - return $item; - }); - } } diff --git a/app/Repositories/Repository.php b/app/Repositories/Repository.php index c209e33ba..3cc0863dd 100644 --- a/app/Repositories/Repository.php +++ b/app/Repositories/Repository.php @@ -2,57 +2,35 @@ namespace Pterodactyl\Repositories; -use InvalidArgumentException; use Illuminate\Foundation\Application; +use Illuminate\Database\Eloquent\Model; use Pterodactyl\Contracts\Repository\RepositoryInterface; abstract class Repository implements RepositoryInterface { - /** - * @var \Illuminate\Foundation\Application - */ - protected $app; + protected array $columns = ['*']; - /** - * @var array - */ - protected $columns = ['*']; + protected Model $model; - /** - * @var mixed - */ - protected $model; - - /** - * @var bool - */ - protected $withFresh = true; + protected bool $withFresh = true; /** * Repository constructor. - * - * @param \Illuminate\Foundation\Application $application */ - public function __construct(Application $application) + public function __construct(protected Application $app) { - $this->app = $application; - $this->initializeModel($this->model()); } /** * Return the model backing this repository. - * - * @return string|\Closure|object */ - abstract public function model(); + abstract public function model(): string; /** * Return the model being used for this repository. - * - * @return mixed */ - public function getModel() + public function getModel(): Model { return $this->model; } @@ -61,9 +39,8 @@ abstract class Repository implements RepositoryInterface * Setup column selection functionality. * * @param array|string $columns - * @return $this */ - public function setColumns($columns = ['*']) + public function setColumns($columns = ['*']): self { $clone = clone $this; $clone->columns = is_array($columns) ? $columns : func_get_args(); @@ -73,10 +50,8 @@ abstract class Repository implements RepositoryInterface /** * Return the columns to be selected in the repository call. - * - * @return array */ - public function getColumns() + public function getColumns(): array { return $this->columns; } @@ -84,32 +59,25 @@ abstract class Repository implements RepositoryInterface /** * Stop repository update functions from returning a fresh * model when changes are committed. - * - * @return $this */ - public function withoutFreshModel() + public function withoutFreshModel(): self { return $this->setFreshModel(false); } /** * Return a fresh model with a repository updates a model. - * - * @return $this */ - public function withFreshModel() + public function withFreshModel(): self { - return $this->setFreshModel(true); + return $this->setFreshModel(); } /** - * Set whether or not the repository should return a fresh model + * Set whether the repository should return a fresh model * when changes are committed. - * - * @param bool $fresh - * @return $this */ - public function setFreshModel(bool $fresh = true) + public function setFreshModel(bool $fresh = true): self { $clone = clone $this; $clone->withFresh = $fresh; @@ -119,11 +87,8 @@ abstract class Repository implements RepositoryInterface /** * Take the provided model and make it accessible to the rest of the repository. - * - * @param array $model - * @return mixed */ - protected function initializeModel(...$model) + protected function initializeModel(string ...$model): mixed { switch (count($model)) { case 1: @@ -131,7 +96,7 @@ abstract class Repository implements RepositoryInterface case 2: return $this->model = call_user_func([$this->app->make($model[0]), $model[1]]); default: - throw new InvalidArgumentException('Model must be a FQDN or an array with a count of two.'); + throw new \InvalidArgumentException('Model must be a FQDN or an array with a count of two.'); } } } diff --git a/app/Repositories/Wings/DaemonBackupRepository.php b/app/Repositories/Wings/DaemonBackupRepository.php new file mode 100644 index 000000000..4c164ee6b --- /dev/null +++ b/app/Repositories/Wings/DaemonBackupRepository.php @@ -0,0 +1,93 @@ +adapter = $adapter; + + return $this; + } + + /** + * Tells the remote Daemon to begin generating a backup for the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function backup(Backup $backup): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/backup', $this->server->uuid), + [ + 'json' => [ + 'adapter' => $this->adapter ?? config('backups.default'), + 'uuid' => $backup->uuid, + 'ignore' => implode("\n", $backup->ignored_files), + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Sends a request to Wings to begin restoring a backup for a server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function restore(Backup $backup, string $url = null, bool $truncate = false): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/backup/%s/restore', $this->server->uuid, $backup->uuid), + [ + 'json' => [ + 'adapter' => $backup->disk, + 'truncate_directory' => $truncate, + 'download_url' => $url ?? '', + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Deletes a backup from the daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function delete(Backup $backup): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->delete( + sprintf('/api/servers/%s/backup/%s', $this->server->uuid, $backup->uuid) + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonCommandRepository.php b/app/Repositories/Wings/DaemonCommandRepository.php new file mode 100644 index 000000000..cde29ff36 --- /dev/null +++ b/app/Repositories/Wings/DaemonCommandRepository.php @@ -0,0 +1,33 @@ +server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/commands', $this->server->uuid), + [ + 'json' => ['commands' => is_array($command) ? $command : [$command]], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonConfigurationRepository.php b/app/Repositories/Wings/DaemonConfigurationRepository.php new file mode 100644 index 000000000..5580f9a8b --- /dev/null +++ b/app/Repositories/Wings/DaemonConfigurationRepository.php @@ -0,0 +1,46 @@ +getHttpClient()->get('/api/system' . (!is_null($version) ? '?v=' . $version : '')); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + return json_decode($response->getBody()->__toString(), true); + } + + /** + * Updates the configuration information for a daemon. Updates the information for + * this instance using a passed-in model. This allows us to change plenty of information + * in the model, and still use the old, pre-update model to actually make the HTTP request. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function update(Node $node): ResponseInterface + { + try { + return $this->getHttpClient()->post( + '/api/update', + ['json' => $node->getConfiguration()] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonFileRepository.php b/app/Repositories/Wings/DaemonFileRepository.php new file mode 100644 index 000000000..ca757151e --- /dev/null +++ b/app/Repositories/Wings/DaemonFileRepository.php @@ -0,0 +1,297 @@ +server, Server::class); + + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/contents', $this->server->uuid), + [ + 'query' => ['file' => $path], + ] + ); + } catch (ClientException|TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + $length = (int) Arr::get($response->getHeader('Content-Length'), 0, 0); + if ($notLargerThan && $length > $notLargerThan) { + throw new FileSizeTooLargeException(); + } + + return $response->getBody()->__toString(); + } + + /** + * Save new contents to a given file. This works for both creating and updating + * a file. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function putContent(string $path, string $content): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/write', $this->server->uuid), + [ + 'query' => ['file' => $path], + 'body' => $content, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Return a directory listing for a given path. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function getDirectory(string $path): array + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/list-directory', $this->server->uuid), + [ + 'query' => ['directory' => $path], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + return json_decode($response->getBody(), true); + } + + /** + * Creates a new directory for the server in the given $path. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function createDirectory(string $name, string $path): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/create-directory', $this->server->uuid), + [ + 'json' => [ + 'name' => $name, + 'path' => $path, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Renames or moves a file on the remote machine. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function renameFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->put( + sprintf('/api/servers/%s/files/rename', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Copy a given file and give it a unique name. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function copyFile(string $location): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/copy', $this->server->uuid), + [ + 'json' => [ + 'location' => $location, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Delete a file or folder for the server. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function deleteFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/delete', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Compress the given files or folders in the given root. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function compressFiles(?string $root, array $files): array + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $response = $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/compress', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + // Wait for up to 15 minutes for the archive to be completed when calling this endpoint + // since it will likely take quite awhile for large directories. + 'timeout' => 60 * 15, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + + return json_decode($response->getBody(), true); + } + + /** + * Decompresses a given archive file. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function decompressFile(?string $root, string $file): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/decompress', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'file' => $file, + ], + // Wait for up to 15 minutes for the decompress to be completed when calling this endpoint + // since it will likely take quite awhile for large directories. + 'timeout' => 60 * 15, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Chmods the given files. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function chmodFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/chmod', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Pulls a file from the given URL and saves it to the disk. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function pull(string $url, ?string $directory, array $params = []): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + $attributes = [ + 'url' => $url, + 'root' => $directory ?? '/', + 'file_name' => $params['filename'] ?? null, + 'use_header' => $params['use_header'] ?? null, + 'foreground' => $params['foreground'] ?? null, + ]; + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/pull', $this->server->uuid), + [ + 'json' => array_filter($attributes, fn ($value) => !is_null($value)), + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonPowerRepository.php b/app/Repositories/Wings/DaemonPowerRepository.php new file mode 100644 index 000000000..4e290cfc9 --- /dev/null +++ b/app/Repositories/Wings/DaemonPowerRepository.php @@ -0,0 +1,31 @@ +server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/power', $this->server->uuid), + ['json' => ['action' => $action]] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonRepository.php b/app/Repositories/Wings/DaemonRepository.php new file mode 100644 index 000000000..512626d73 --- /dev/null +++ b/app/Repositories/Wings/DaemonRepository.php @@ -0,0 +1,65 @@ +server = $server; + + $this->setNode($this->server->node); + + return $this; + } + + /** + * Set the node model this request is stemming from. + */ + public function setNode(Node $node): self + { + $this->node = $node; + + return $this; + } + + /** + * Return an instance of the Guzzle HTTP Client to be used for requests. + */ + public function getHttpClient(array $headers = []): Client + { + Assert::isInstanceOf($this->node, Node::class); + + return new Client([ + 'verify' => $this->app->environment('production'), + 'base_uri' => $this->node->getConnectionAddress(), + 'timeout' => config('pterodactyl.guzzle.timeout'), + 'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'), + 'headers' => array_merge($headers, [ + 'Authorization' => 'Bearer ' . $this->node->getDecryptedKey(), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ]), + ]); + } +} diff --git a/app/Repositories/Wings/DaemonServerRepository.php b/app/Repositories/Wings/DaemonServerRepository.php new file mode 100644 index 000000000..f013ac08c --- /dev/null +++ b/app/Repositories/Wings/DaemonServerRepository.php @@ -0,0 +1,158 @@ +server, Server::class); + + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s', $this->server->uuid) + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception, false); + } + + return json_decode($response->getBody()->__toString(), true); + } + + /** + * Creates a new server on the Wings daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function create(bool $startOnCompletion = true): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post('/api/servers', [ + 'json' => [ + 'uuid' => $this->server->uuid, + 'start_on_completion' => $startOnCompletion, + ], + ]); + } catch (GuzzleException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Triggers a server sync on Wings. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function sync(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post("/api/servers/{$this->server->uuid}/sync"); + } catch (GuzzleException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Delete a server from the daemon, forcibly if passed. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function delete(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->delete('/api/servers/' . $this->server->uuid); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Reinstall a server on the daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function reinstall(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post(sprintf( + '/api/servers/%s/reinstall', + $this->server->uuid + )); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Requests the daemon to create a full archive of the server. Once the daemon is finished + * they will send a POST request to "/api/remote/servers/{uuid}/archive" with a boolean. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function requestArchive(): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post(sprintf( + '/api/servers/%s/archive', + $this->server->uuid + )); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } + + /** + * Revokes a single user's JTI by using their ID. This is simply a helper function to + * make it easier to revoke tokens on the fly. This ensures that the JTI key is formatted + * correctly and avoids any costly mistakes in the codebase. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function revokeUserJTI(int $id): void + { + Assert::isInstanceOf($this->server, Server::class); + + $this->revokeJTIs([md5($id . $this->server->uuid)]); + } + + /** + * Revokes an array of JWT JTI's by marking any token generated before the current time on + * the Wings instance as being invalid. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + protected function revokeJTIs(array $jtis): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient() + ->post(sprintf('/api/servers/%s/ws/deny', $this->server->uuid), [ + 'json' => ['jtis' => $jtis], + ]); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Repositories/Wings/DaemonTransferRepository.php b/app/Repositories/Wings/DaemonTransferRepository.php new file mode 100644 index 000000000..9c8745232 --- /dev/null +++ b/app/Repositories/Wings/DaemonTransferRepository.php @@ -0,0 +1,33 @@ +getHttpClient()->post(sprintf('/api/servers/%s/transfer', $this->server->uuid), [ + 'json' => [ + 'server_id' => $this->server->uuid, + 'url' => $targetNode->getConnectionAddress() . '/api/transfers', + 'token' => 'Bearer ' . $token->toString(), + 'server' => [ + 'uuid' => $this->server->uuid, + 'start_on_completion' => false, + ], + ], + ]); + } catch (GuzzleException $exception) { + throw new DaemonConnectionException($exception); + } + } +} diff --git a/app/Rules/Fqdn.php b/app/Rules/Fqdn.php new file mode 100644 index 000000000..915ca4015 --- /dev/null +++ b/app/Rules/Fqdn.php @@ -0,0 +1,80 @@ +data = $data; + + return $this; + } + + /** + * Validates that the value provided resolves to an IP address. If a scheme is + * specified when this rule is created additional checks will be applied. + * + * @param string $attribute + * @param mixed $value + */ + public function passes($attribute, $value): bool + { + if (filter_var($value, FILTER_VALIDATE_IP)) { + // Check if the scheme is set to HTTPS. + // + // Unless someone owns their IP blocks and decides to pay who knows how much for a + // custom SSL cert, IPs will not be able to use HTTPS. This should prevent most + // home users from making this mistake and wondering why their node is not working. + if ($this->schemeField && Arr::get($this->data, $this->schemeField) === 'https') { + $this->message = 'The :attribute must not be an IP address when HTTPS is enabled.'; + + return false; + } + + return true; + } + + // Lookup A and AAAA DNS records for the FQDN. Note, this function will also resolve CNAMEs + // for us automatically, there is no need to manually resolve them here. + // + // The error suppression is intentional, see https://bugs.php.net/bug.php?id=73149 + $records = @dns_get_record($value, DNS_A + DNS_AAAA); + // If no records were returned fall back to trying to resolve the value using the hosts DNS + // resolution. This will not work for IPv6 which is why we prefer to use `dns_get_record` + // first. + if (!empty($records) || filter_var(gethostbyname($value), FILTER_VALIDATE_IP)) { + return true; + } + + $this->message = 'The :attribute could not be resolved to a valid IP address.'; + + return false; + } + + public function message(): string + { + return $this->message; + } + + /** + * Returns a new instance of the rule with a defined scheme set. + */ + public static function make(string $schemeField = null): self + { + return tap(new static(), function ($fqdn) use ($schemeField) { + $fqdn->schemeField = $schemeField; + }); + } +} diff --git a/app/Rules/Username.php b/app/Rules/Username.php index 335c3c186..b89184e9a 100644 --- a/app/Rules/Username.php +++ b/app/Rules/Username.php @@ -13,13 +13,12 @@ class Username implements Rule /** * Validate that a username contains only the allowed characters and starts/ends - * with alpha-numeric characters. + * with alphanumeric characters. * * Allowed characters: a-z0-9_-. * * @param string $attribute - * @param mixed $value - * @return bool + * @param mixed $value */ public function passes($attribute, $value): bool { @@ -28,8 +27,6 @@ class Username implements Rule /** * Return a validation message for use when this rule fails. - * - * @return string */ public function message(): string { @@ -40,10 +37,8 @@ class Username implements Rule /** * Convert the rule to a validation string. This is necessary to avoid * issues with Eloquence which tries to use this rule as a string. - * - * @return string */ - public function __toString() + public function __toString(): string { return 'p_username'; } diff --git a/app/Services/Acl/Api/AdminAcl.php b/app/Services/Acl/Api/AdminAcl.php index 6dfa87971..cee076e17 100644 --- a/app/Services/Acl/Api/AdminAcl.php +++ b/app/Services/Acl/Api/AdminAcl.php @@ -2,7 +2,6 @@ namespace Pterodactyl\Services\Acl\Api; -use ReflectionClass; use Pterodactyl\Models\ApiKey; class AdminAcl @@ -11,39 +10,34 @@ class AdminAcl * Resource permission columns in the api_keys table begin * with this identifier. */ - const COLUMN_IDENTIFIER = 'r_'; + public const COLUMN_IDENTIFIER = 'r_'; /** * The different types of permissions available for API keys. This * implements a read/write/none permissions scheme for all endpoints. */ - const NONE = 0; - const READ = 1; - const WRITE = 2; + public const NONE = 0; + public const READ = 1; + public const WRITE = 2; /** * Resources that are available on the API and can contain a permissions * set for each key. These are stored in the database as r_{resource}. */ - const RESOURCE_SERVERS = 'servers'; - const RESOURCE_NODES = 'nodes'; - const RESOURCE_ALLOCATIONS = 'allocations'; - const RESOURCE_USERS = 'users'; - const RESOURCE_LOCATIONS = 'locations'; - const RESOURCE_NESTS = 'nests'; - const RESOURCE_EGGS = 'eggs'; - const RESOURCE_DATABASE_HOSTS = 'database_hosts'; - const RESOURCE_SERVER_DATABASES = 'server_databases'; - const RESOURCE_PACKS = 'packs'; + public const RESOURCE_SERVERS = 'servers'; + public const RESOURCE_NODES = 'nodes'; + public const RESOURCE_ALLOCATIONS = 'allocations'; + public const RESOURCE_USERS = 'users'; + public const RESOURCE_LOCATIONS = 'locations'; + public const RESOURCE_NESTS = 'nests'; + public const RESOURCE_EGGS = 'eggs'; + public const RESOURCE_DATABASE_HOSTS = 'database_hosts'; + public const RESOURCE_SERVER_DATABASES = 'server_databases'; /** * Determine if an API key has permission to perform a specific read/write operation. - * - * @param int $permission - * @param int $action - * @return bool */ - public static function can(int $permission, int $action = self::READ) + public static function can(int $permission, int $action = self::READ): bool { if ($permission & $action) { return true; @@ -55,13 +49,8 @@ class AdminAcl /** * Determine if an API Key model has permission to access a given resource * at a specific action level. - * - * @param \Pterodactyl\Models\ApiKey $key - * @param string $resource - * @param int $action - * @return bool */ - public static function check(ApiKey $key, string $resource, int $action = self::READ) + public static function check(ApiKey $key, string $resource, int $action = self::READ): bool { return self::can(data_get($key, self::COLUMN_IDENTIFIER . $resource, self::NONE), $action); } @@ -69,12 +58,11 @@ class AdminAcl /** * Return a list of all resource constants defined in this ACL. * - * @return array * @throws \ReflectionException */ public static function getResourceList(): array { - $reflect = new ReflectionClass(__CLASS__); + $reflect = new \ReflectionClass(__CLASS__); return collect($reflect->getConstants())->filter(function ($value, $key) { return substr($key, 0, 9) === 'RESOURCE_'; diff --git a/app/Services/Activity/ActivityLogBatchService.php b/app/Services/Activity/ActivityLogBatchService.php new file mode 100644 index 000000000..f4206ea56 --- /dev/null +++ b/app/Services/Activity/ActivityLogBatchService.php @@ -0,0 +1,59 @@ +uuid; + } + + /** + * Starts a new batch transaction. If there is already a transaction present + * this will be nested. + */ + public function start(): void + { + if ($this->transaction === 0) { + $this->uuid = Uuid::uuid4()->toString(); + } + + ++$this->transaction; + } + + /** + * Ends a batch transaction, if this is the last transaction in the stack + * the UUID will be cleared out. + */ + public function end(): void + { + $this->transaction = max(0, $this->transaction - 1); + + if ($this->transaction === 0) { + $this->uuid = null; + } + } + + /** + * Executes the logic provided within the callback in the scope of an activity + * log batch transaction. + */ + public function transaction(\Closure $callback): mixed + { + $this->start(); + $result = $callback($this->uuid()); + $this->end(); + + return $result; + } +} diff --git a/app/Services/Activity/ActivityLogService.php b/app/Services/Activity/ActivityLogService.php new file mode 100644 index 000000000..f86385214 --- /dev/null +++ b/app/Services/Activity/ActivityLogService.php @@ -0,0 +1,254 @@ +getActivity()->actor_id = null; + $this->getActivity()->actor_type = null; + $this->getActivity()->setRelation('actor', null); + + return $this; + } + + /** + * Sets the action for this activity log. + */ + public function event(string $action): self + { + $this->getActivity()->event = $action; + + return $this; + } + + /** + * Set the description for this activity. + */ + public function description(?string $description): self + { + $this->getActivity()->description = $description; + + return $this; + } + + /** + * Sets the subject model instance. + * + * @template T extends \Illuminate\Database\Eloquent\Model|\Illuminate\Contracts\Auth\Authenticatable + * + * @param T|T[]|null $subjects + */ + public function subject(...$subjects): self + { + foreach (Arr::wrap($subjects) as $subject) { + if (is_null($subject)) { + continue; + } + + foreach ($this->subjects as $entry) { + // If this subject is already tracked in our array of subjects just skip over + // it and move on to the next one in the list. + if ($entry->is($subject)) { + continue 2; + } + } + + $this->subjects[] = $subject; + } + + return $this; + } + + /** + * Sets the actor model instance. + */ + public function actor(Model $actor): self + { + $this->getActivity()->actor()->associate($actor); + + return $this; + } + + /** + * Sets a custom property on the activity log instance. + * + * @param string|array $key + * @param mixed $value + */ + public function property($key, $value = null): self + { + $properties = $this->getActivity()->properties; + $this->activity->properties = is_array($key) + ? $properties->merge($key) + : $properties->put($key, $value); + + return $this; + } + + /** + * Attaches the instance request metadata to the activity log event. + */ + public function withRequestMetadata(): self + { + return $this->property([ + 'ip' => Request::getClientIp(), + 'useragent' => Request::userAgent(), + ]); + } + + /** + * Logs an activity log entry with the set values and then returns the + * model instance to the caller. If there is an exception encountered while + * performing this action it will be logged to the disk but will not interrupt + * the code flow. + */ + public function log(string $description = null): ActivityLog + { + $activity = $this->getActivity(); + + if (!is_null($description)) { + $activity->description = $description; + } + + try { + return $this->save(); + } catch (\Throwable|\Exception $exception) { + if (config('app.env') !== 'production') { + /* @noinspection PhpUnhandledExceptionInspection */ + throw $exception; + } + + Log::error($exception); + } + + return $activity; + } + + /** + * Returns a cloned instance of the service allowing for the creation of a base + * activity log with the ability to change values on the fly without impact. + */ + public function clone(): self + { + return clone $this; + } + + /** + * Executes the provided callback within the scope of a database transaction + * and will only save the activity log entry if everything else successfully + * settles. + * + * @throws \Throwable + */ + public function transaction(\Closure $callback) + { + return $this->connection->transaction(function () use ($callback) { + $response = $callback($this); + + $this->save(); + + return $response; + }); + } + + /** + * Resets the instance and clears out the log. + */ + public function reset(): void + { + $this->activity = null; + $this->subjects = []; + } + + /** + * Returns the current activity log instance. + */ + protected function getActivity(): ActivityLog + { + if ($this->activity) { + return $this->activity; + } + + $this->activity = new ActivityLog([ + 'ip' => Request::ip(), + 'batch_uuid' => $this->batch->uuid(), + 'properties' => Collection::make([]), + 'api_key_id' => $this->targetable->apiKeyId(), + ]); + + if ($subject = $this->targetable->subject()) { + $this->subject($subject); + } + + if ($actor = $this->targetable->actor()) { + $this->actor($actor); + } elseif ($user = $this->manager->guard()->user()) { + if ($user instanceof Model) { + $this->actor($user); + } + } + + return $this->activity; + } + + /** + * Saves the activity log instance and attaches all of the subject models. + * + * @throws \Throwable + */ + protected function save(): ActivityLog + { + Assert::notNull($this->activity); + + $response = $this->connection->transaction(function () { + $this->activity->save(); + + $subjects = Collection::make($this->subjects) + ->map(fn (Model $subject) => [ + 'activity_log_id' => $this->activity->id, + 'subject_id' => $subject->getKey(), + 'subject_type' => $subject->getMorphClass(), + ]) + ->values() + ->toArray(); + + ActivityLogSubject::insert($subjects); + + return $this->activity; + }); + + $this->activity = null; + $this->subjects = []; + + return $response; + } +} diff --git a/app/Services/Activity/ActivityLogTargetableService.php b/app/Services/Activity/ActivityLogTargetableService.php new file mode 100644 index 000000000..4d37e9828 --- /dev/null +++ b/app/Services/Activity/ActivityLogTargetableService.php @@ -0,0 +1,51 @@ +actor = $actor; + } + + public function setSubject(Model $subject): void + { + $this->subject = $subject; + } + + public function setApiKeyId(?int $apiKeyId): void + { + $this->apiKeyId = $apiKeyId; + } + + public function actor(): ?Model + { + return $this->actor; + } + + public function subject(): ?Model + { + return $this->subject; + } + + public function apiKeyId(): ?int + { + return $this->apiKeyId; + } + + public function reset(): void + { + $this->actor = null; + $this->subject = null; + $this->apiKeyId = null; + } +} diff --git a/app/Services/Allocations/AllocationDeletionService.php b/app/Services/Allocations/AllocationDeletionService.php index 5e81a1d2f..8ea677245 100644 --- a/app/Services/Allocations/AllocationDeletionService.php +++ b/app/Services/Allocations/AllocationDeletionService.php @@ -8,33 +8,22 @@ use Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException; class AllocationDeletionService { - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - private $repository; - /** * AllocationDeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository */ - public function __construct(AllocationRepositoryInterface $repository) + public function __construct(private AllocationRepositoryInterface $repository) { - $this->repository = $repository; } /** * Delete an allocation from the database only if it does not have a server * that is actively attached to it. * - * @param \Pterodactyl\Models\Allocation $allocation - * @return int - * * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException */ - public function handle(Allocation $allocation) + public function handle(Allocation $allocation): int { - if (! is_null($allocation->server_id)) { + if (!is_null($allocation->server_id)) { throw new ServerUsingAllocationException(trans('exceptions.allocations.server_using')); } diff --git a/app/Services/Allocations/AssignmentService.php b/app/Services/Allocations/AssignmentService.php index 905bb776f..19c161fd8 100644 --- a/app/Services/Allocations/AssignmentService.php +++ b/app/Services/Allocations/AssignmentService.php @@ -5,6 +5,7 @@ namespace Pterodactyl\Services\Allocations; use IPTools\Network; use Pterodactyl\Models\Node; use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; use Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException; use Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException; @@ -13,59 +14,55 @@ use Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException; class AssignmentService { - const CIDR_MAX_BITS = 27; - const CIDR_MIN_BITS = 32; - const PORT_FLOOR = 1024; - const PORT_CEIL = 65535; - const PORT_RANGE_LIMIT = 1000; - const PORT_RANGE_REGEX = '/^(\d{4,5})-(\d{4,5})$/'; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - protected $repository; + public const CIDR_MAX_BITS = 27; + public const CIDR_MIN_BITS = 32; + public const PORT_FLOOR = 1024; + public const PORT_CEIL = 65535; + public const PORT_RANGE_LIMIT = 1000; + public const PORT_RANGE_REGEX = '/^(\d{4,5})-(\d{4,5})$/'; /** * AssignmentService constructor. - * - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository - * @param \Illuminate\Database\ConnectionInterface $connection */ - public function __construct(AllocationRepositoryInterface $repository, ConnectionInterface $connection) + public function __construct(protected AllocationRepositoryInterface $repository, protected ConnectionInterface $connection) { - $this->connection = $connection; - $this->repository = $repository; } /** * Insert allocations into the database and link them to a specific node. * - * @param \Pterodactyl\Models\Node $node - * @param array $data - * + * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException - * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException + * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException */ - public function handle(Node $node, array $data) + public function handle(Node $node, array $data): void { - $explode = explode('/', $data['allocation_ip']); + $allocationIp = $data['allocation_ip']; + $explode = explode('/', $allocationIp); if (count($explode) !== 1) { - if (! ctype_digit($explode[1]) || ($explode[1] > self::CIDR_MIN_BITS || $explode[1] < self::CIDR_MAX_BITS)) { - throw new CidrOutOfRangeException; + if (!ctype_digit($explode[1]) || ($explode[1] > self::CIDR_MIN_BITS || $explode[1] < self::CIDR_MAX_BITS)) { + throw new CidrOutOfRangeException(); } } + $underlying = 'Unknown IP'; + try { + // TODO: how should we approach supporting IPv6 with this? + // gethostbyname only supports IPv4, but the alternative (dns_get_record) returns + // an array of records, which is not ideal for this use case, we need a SINGLE + // IP to use, not multiple. + $underlying = gethostbyname($allocationIp); + $parsed = Network::parse($underlying); + } catch (\Exception $exception) { + throw new DisplayException("Could not parse provided allocation IP address for $allocationIp ($underlying): {$exception->getMessage()}", $exception); + } + $this->connection->beginTransaction(); - foreach (Network::parse(gethostbyname($data['allocation_ip'])) as $ip) { + foreach ($parsed as $ip) { foreach ($data['allocation_ports'] as $port) { - if (! is_digit($port) && ! preg_match(self::PORT_RANGE_REGEX, $port)) { + if (!is_digit($port) && !preg_match(self::PORT_RANGE_REGEX, $port)) { throw new InvalidPortMappingException($port); } @@ -74,11 +71,11 @@ class AssignmentService $block = range($matches[1], $matches[2]); if (count($block) > self::PORT_RANGE_LIMIT) { - throw new TooManyPortsInRangeException; + throw new TooManyPortsInRangeException(); } if ((int) $matches[1] <= self::PORT_FLOOR || (int) $matches[2] > self::PORT_CEIL) { - throw new PortOutOfRangeException; + throw new PortOutOfRangeException(); } foreach ($block as $unit) { @@ -92,7 +89,7 @@ class AssignmentService } } else { if ((int) $port <= self::PORT_FLOOR || (int) $port > self::PORT_CEIL) { - throw new PortOutOfRangeException; + throw new PortOutOfRangeException(); } $insertData[] = [ diff --git a/app/Services/Allocations/FindAssignableAllocationService.php b/app/Services/Allocations/FindAssignableAllocationService.php new file mode 100644 index 000000000..3374fa07e --- /dev/null +++ b/app/Services/Allocations/FindAssignableAllocationService.php @@ -0,0 +1,111 @@ +node->allocations() + ->where('ip', $server->allocation->ip) + ->whereNull('server_id') + ->inRandomOrder() + ->first(); + + $allocation = $allocation ?? $this->createNewAllocation($server); + + $allocation->update(['server_id' => $server->id]); + + return $allocation->refresh(); + } + + /** + * Create a new allocation on the server's node with a random port from the defined range + * in the settings. If there are no matches in that range, or something is wrong with the + * range information provided an exception will be raised. + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException + * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException + * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException + */ + protected function createNewAllocation(Server $server): Allocation + { + $start = config('pterodactyl.client_features.allocations.range_start', null); + $end = config('pterodactyl.client_features.allocations.range_end', null); + + if (!$start || !$end) { + throw new NoAutoAllocationSpaceAvailableException(); + } + + Assert::integerish($start); + Assert::integerish($end); + + // Get all of the currently allocated ports for the node so that we can figure out + // which port might be available. + $ports = $server->node->allocations() + ->where('ip', $server->allocation->ip) + ->whereBetween('port', [$start, $end]) + ->pluck('port'); + + // Compute the difference of the range and the currently created ports, finding + // any port that does not already exist in the database. We will then use this + // array of ports to create a new allocation to assign to the server. + $available = array_diff(range($start, $end), $ports->toArray()); + + // If we've already allocated all of the ports, just abort. + if (empty($available)) { + throw new NoAutoAllocationSpaceAvailableException(); + } + + // Pick a random port out of the remaining available ports. + /** @var int $port */ + $port = $available[array_rand($available)]; + + $this->service->handle($server->node, [ + 'allocation_ip' => $server->allocation->ip, + 'allocation_ports' => [$port], + ]); + + /** @var \Pterodactyl\Models\Allocation $allocation */ + $allocation = $server->node->allocations() + ->where('ip', $server->allocation->ip) + ->where('port', $port) + ->firstOrFail(); + + return $allocation; + } +} diff --git a/app/Services/Allocations/SetDefaultAllocationService.php b/app/Services/Allocations/SetDefaultAllocationService.php deleted file mode 100644 index 6e9010316..000000000 --- a/app/Services/Allocations/SetDefaultAllocationService.php +++ /dev/null @@ -1,110 +0,0 @@ -connection = $connection; - $this->daemonRepository = $daemonRepository; - $this->repository = $repository; - $this->serverRepository = $serverRepository; - } - - /** - * Update the default allocation for a server only if that allocation is currently - * assigned to the specified server. - * - * @param int|\Pterodactyl\Models\Server $server - * @param int $allocation - * @return \Pterodactyl\Models\Allocation - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException - */ - public function handle($server, int $allocation): Allocation - { - if (! $server instanceof Server) { - $server = $this->serverRepository->find($server); - } - - $allocations = $this->repository->findWhere([['server_id', '=', $server->id]]); - $model = $allocations->filter(function ($model) use ($allocation) { - return $model->id === $allocation; - })->first(); - - if (! $model instanceof Allocation) { - throw new AllocationDoesNotBelongToServerException; - } - - $this->connection->beginTransaction(); - $this->serverRepository->withoutFreshModel()->update($server->id, ['allocation_id' => $model->id]); - - // Update on the daemon. - try { - $this->daemonRepository->setServer($server)->update([ - 'build' => [ - 'default' => [ - 'ip' => $model->ip, - 'port' => $model->port, - ], - 'ports|overwrite' => $allocations->groupBy('ip')->map(function ($item) { - return $item->pluck('port'); - })->toArray(), - ], - ]); - - $this->connection->commit(); - } catch (RequestException $exception) { - $this->connection->rollBack(); - throw new DaemonConnectionException($exception); - } - - return $model; - } -} diff --git a/app/Services/Api/KeyCreationService.php b/app/Services/Api/KeyCreationService.php index b51431620..f026b9f22 100644 --- a/app/Services/Api/KeyCreationService.php +++ b/app/Services/Api/KeyCreationService.php @@ -8,41 +8,20 @@ use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface; class KeyCreationService { - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var int - */ - private $keyType = ApiKey::TYPE_NONE; - - /** - * @var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface - */ - private $repository; + private int $keyType = ApiKey::TYPE_NONE; /** * ApiKeyService constructor. - * - * @param \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface $repository - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter */ - public function __construct(ApiKeyRepositoryInterface $repository, Encrypter $encrypter) + public function __construct(private ApiKeyRepositoryInterface $repository, private Encrypter $encrypter) { - $this->encrypter = $encrypter; - $this->repository = $repository; } /** * Set the type of key that should be created. By default an orphaned key will be * created. These keys cannot be used for anything, and will not render in the UI. - * - * @param int $type - * @return \Pterodactyl\Services\Api\KeyCreationService */ - public function setKeyType(int $type) + public function setKeyType(int $type): self { $this->keyType = $type; @@ -54,17 +33,13 @@ class KeyCreationService * This will automatically generate an identifier and an encrypted token that are * stored in the database. * - * @param array $data - * @param array $permissions - * @return \Pterodactyl\Models\ApiKey - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ public function handle(array $data, array $permissions = []): ApiKey { $data = array_merge($data, [ 'key_type' => $this->keyType, - 'identifier' => str_random(ApiKey::IDENTIFIER_LENGTH), + 'identifier' => ApiKey::generateTokenIdentifier($this->keyType), 'token' => $this->encrypter->encrypt(str_random(ApiKey::KEY_LENGTH)), ]); @@ -72,8 +47,6 @@ class KeyCreationService $data = array_merge($data, $permissions); } - $instance = $this->repository->create($data, true, true); - - return $instance; + return $this->repository->create($data, true, true); } } diff --git a/app/Services/Backups/DeleteBackupService.php b/app/Services/Backups/DeleteBackupService.php new file mode 100644 index 000000000..bbb54ce6a --- /dev/null +++ b/app/Services/Backups/DeleteBackupService.php @@ -0,0 +1,85 @@ +is_locked && ($backup->is_successful && !is_null($backup->completed_at))) { + throw new BackupLockedException(); + } + + if ($backup->disk === Backup::ADAPTER_AWS_S3) { + $this->deleteFromS3($backup); + + return; + } + + $this->connection->transaction(function () use ($backup) { + try { + $this->daemonBackupRepository->setServer($backup->server)->delete($backup); + } catch (DaemonConnectionException $exception) { + $previous = $exception->getPrevious(); + // Don't fail the request if the Daemon responds with a 404, just assume the backup + // doesn't actually exist and remove its reference from the Panel as well. + if (!$previous instanceof ClientException || $previous->getResponse()->getStatusCode() !== Response::HTTP_NOT_FOUND) { + throw $exception; + } + } + + $backup->delete(); + }); + } + + /** + * Deletes a backup from an S3 disk. + * + * @throws \Throwable + */ + protected function deleteFromS3(Backup $backup): void + { + $this->connection->transaction(function () use ($backup) { + $backup->delete(); + + /** @var \Pterodactyl\Extensions\Filesystem\S3Filesystem $adapter */ + $adapter = $this->manager->adapter(Backup::ADAPTER_AWS_S3); + + /** @var \Aws\S3\S3Client $client */ + $client = $adapter->getClient(); + + $client->deleteObject([ + 'Bucket' => $adapter->getBucket(), + 'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid), + ]); + }); + } +} diff --git a/app/Services/Backups/DownloadLinkService.php b/app/Services/Backups/DownloadLinkService.php new file mode 100644 index 000000000..f3f76c845 --- /dev/null +++ b/app/Services/Backups/DownloadLinkService.php @@ -0,0 +1,62 @@ +disk === Backup::ADAPTER_AWS_S3) { + return $this->getS3BackupUrl($backup); + } + + $token = $this->jwtService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setUser($user) + ->setClaims([ + 'backup_uuid' => $backup->uuid, + 'server_uuid' => $backup->server->uuid, + ]) + ->handle($backup->server->node, $user->id . $backup->server->uuid); + + return sprintf('%s/download/backup?token=%s', $backup->server->node->getConnectionAddress(), $token->toString()); + } + + /** + * Returns a signed URL that allows us to download a file directly out of a non-public + * S3 bucket by using a signed URL. + */ + protected function getS3BackupUrl(Backup $backup): string + { + /** @var \Pterodactyl\Extensions\Filesystem\S3Filesystem $adapter */ + $adapter = $this->backupManager->adapter(Backup::ADAPTER_AWS_S3); + + $request = $adapter->getClient()->createPresignedRequest( + $adapter->getClient()->getCommand('GetObject', [ + 'Bucket' => $adapter->getBucket(), + 'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid), + 'ContentType' => 'application/x-gzip', + ]), + CarbonImmutable::now()->addMinutes(5) + ); + + return $request->getUri()->__toString(); + } +} diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php new file mode 100644 index 000000000..f14275403 --- /dev/null +++ b/app/Services/Backups/InitiateBackupService.php @@ -0,0 +1,128 @@ +isLocked = $isLocked; + + return $this; + } + + /** + * Sets the files to be ignored by this backup. + * + * @param string[]|null $ignored + */ + public function setIgnoredFiles(?array $ignored): self + { + if (is_array($ignored)) { + foreach ($ignored as $value) { + Assert::string($value); + } + } + + // Set the ignored files to be any values that are not empty in the array. Don't use + // the PHP empty function here incase anything that is "empty" by default (0, false, etc.) + // were passed as a file or folder name. + $this->ignoredFiles = is_null($ignored) ? [] : array_filter($ignored, function ($value) { + return strlen($value) > 0; + }); + + return $this; + } + + /** + * Initiates the backup process for a server on Wings. + * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException + * @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException + */ + public function handle(Server $server, string $name = null, bool $override = false): Backup + { + $limit = config('backups.throttles.limit'); + $period = config('backups.throttles.period'); + if ($period > 0) { + $previous = $this->repository->getBackupsGeneratedDuringTimespan($server->id, $period); + if ($previous->count() >= $limit) { + $message = sprintf('Only %d backups may be generated within a %d second span of time.', $limit, $period); + + throw new TooManyRequestsHttpException(CarbonImmutable::now()->diffInSeconds($previous->last()->created_at->addSeconds($period)), $message); + } + } + + // Check if the server has reached or exceeded its backup limit. + // completed_at == null will cover any ongoing backups, while is_successful == true will cover any completed backups. + $successful = $this->repository->getNonFailedBackups($server); + if (!$server->backup_limit || $successful->count() >= $server->backup_limit) { + // Do not allow the user to continue if this server is already at its limit and can't override. + if (!$override || $server->backup_limit <= 0) { + throw new TooManyBackupsException($server->backup_limit); + } + + // Get the oldest backup the server has that is not "locked" (indicating a backup that should + // never be automatically purged). If we find a backup we will delete it and then continue with + // this process. If no backup is found that can be used an exception is thrown. + $oldest = $successful->where('is_locked', false)->orderBy('created_at')->first(); + if (!$oldest) { + throw new TooManyBackupsException($server->backup_limit); + } + + /* @var Backup $oldest */ + $this->deleteBackupService->handle($oldest); + } + + return $this->connection->transaction(function () use ($server, $name) { + /** @var Backup $backup */ + $backup = $this->repository->create([ + 'server_id' => $server->id, + 'uuid' => Uuid::uuid4()->toString(), + 'name' => trim($name) ?: sprintf('Backup at %s', CarbonImmutable::now()->toDateTimeString()), + 'ignored_files' => array_values($this->ignoredFiles ?? []), + 'disk' => $this->backupManager->getDefaultAdapter(), + 'is_locked' => $this->isLocked, + ], true, true); + + $this->daemonBackupRepository->setServer($server) + ->setBackupAdapter($this->backupManager->getDefaultAdapter()) + ->backup($backup); + + return $backup; + }); + } +} diff --git a/app/Services/DaemonKeys/DaemonKeyCreationService.php b/app/Services/DaemonKeys/DaemonKeyCreationService.php deleted file mode 100644 index 23aa1c0aa..000000000 --- a/app/Services/DaemonKeys/DaemonKeyCreationService.php +++ /dev/null @@ -1,87 +0,0 @@ -. - * - * 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. - */ - -namespace Pterodactyl\Services\DaemonKeys; - -use Carbon\Carbon; -use Illuminate\Contracts\Config\Repository as ConfigRepository; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; - -class DaemonKeyCreationService -{ - /** - * @var \Carbon\Carbon - */ - protected $carbon; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface - */ - protected $repository; - - /** - * DaemonKeyCreationService constructor. - * - * @param \Carbon\Carbon $carbon - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository - */ - public function __construct( - Carbon $carbon, - ConfigRepository $config, - DaemonKeyRepositoryInterface $repository - ) { - $this->carbon = $carbon; - $this->config = $config; - $this->repository = $repository; - } - - /** - * Create a new daemon key to be used when connecting to a daemon. - * - * @param int $server - * @param int $user - * @return string - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function handle(int $server, int $user) - { - $secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40); - - $this->repository->withoutFreshModel()->create([ - 'user_id' => $user, - 'server_id' => $server, - 'secret' => $secret, - 'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(), - ]); - - return $secret; - } -} diff --git a/app/Services/DaemonKeys/DaemonKeyDeletionService.php b/app/Services/DaemonKeys/DaemonKeyDeletionService.php deleted file mode 100644 index 054122d82..000000000 --- a/app/Services/DaemonKeys/DaemonKeyDeletionService.php +++ /dev/null @@ -1,124 +0,0 @@ -. - * - * 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. - */ - -namespace Pterodactyl\Services\DaemonKeys; - -use Webmozart\Assert\Assert; -use Pterodactyl\Models\Server; -use Psr\Log\LoggerInterface as Writer; -use GuzzleHttp\Exception\RequestException; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; - -class DaemonKeyDeletionService -{ - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * @var \Psr\Log\LoggerInterface - */ - protected $writer; - - /** - * DaemonKeyDeletionService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Psr\Log\LoggerInterface $writer - */ - public function __construct( - ConnectionInterface $connection, - DaemonKeyRepositoryInterface $repository, - DaemonServerRepositoryInterface $daemonRepository, - ServerRepositoryInterface $serverRepository, - Writer $writer - ) { - $this->connection = $connection; - $this->daemonRepository = $daemonRepository; - $this->repository = $repository; - $this->serverRepository = $serverRepository; - $this->writer = $writer; - } - - /** - * @param \Pterodactyl\Models\Server|int $server - * @param int $user - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle($server, $user) - { - Assert::integerish($user, 'Second argument passed to handle must be an integer, received %s.'); - - if (! $server instanceof Server) { - $server = $this->serverRepository->find($server); - } - - $this->connection->beginTransaction(); - $key = $this->repository->findFirstWhere([ - ['user_id', '=', $user], - ['server_id', '=', $server->id], - ]); - - $this->repository->delete($key->id); - - try { - $this->daemonRepository->setServer($server)->revokeAccessKey($key->secret); - } catch (RequestException $exception) { - $response = $exception->getResponse(); - $this->connection->rollBack(); - $this->writer->warning($exception); - - throw new DisplayException(trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ])); - } - - $this->connection->commit(); - } -} diff --git a/app/Services/DaemonKeys/DaemonKeyProviderService.php b/app/Services/DaemonKeys/DaemonKeyProviderService.php deleted file mode 100644 index a386d286e..000000000 --- a/app/Services/DaemonKeys/DaemonKeyProviderService.php +++ /dev/null @@ -1,121 +0,0 @@ -. - * - * 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. - */ - -namespace Pterodactyl\Services\DaemonKeys; - -use Carbon\Carbon; -use Pterodactyl\Models\User; -use Pterodactyl\Models\Server; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; - -class DaemonKeyProviderService -{ - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService - */ - private $keyCreationService; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService - */ - private $keyUpdateService; - - /** - * @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface - */ - private $subuserRepository; - - /** - * GetDaemonKeyService constructor. - * - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService - * @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService $keyUpdateService - * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository - */ - public function __construct( - DaemonKeyCreationService $keyCreationService, - DaemonKeyRepositoryInterface $repository, - DaemonKeyUpdateService $keyUpdateService, - SubuserRepositoryInterface $subuserRepository - ) { - $this->keyCreationService = $keyCreationService; - $this->keyUpdateService = $keyUpdateService; - $this->repository = $repository; - $this->subuserRepository = $subuserRepository; - } - - /** - * Get the access key for a user on a specific server. - * - * @param \Pterodactyl\Models\Server $server - * @param \Pterodactyl\Models\User $user - * @param bool $updateIfExpired - * @return string - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(Server $server, User $user, $updateIfExpired = true): string - { - try { - $key = $this->repository->findFirstWhere([ - ['user_id', '=', $user->id], - ['server_id', '=', $server->id], - ]); - } catch (RecordNotFoundException $exception) { - // If key doesn't exist but we are an admin or the server owner, - // create it. - if ($user->root_admin || $user->id === $server->owner_id) { - return $this->keyCreationService->handle($server->id, $user->id); - } - - // Check if user is a subuser for this server. Ideally they should always have - // a record associated with them in the database, but we should still handle - // that potentiality here. - // - // If no subuser is found, a RecordNotFoundException will be thrown, thus handling - // the parent error as well. - $subuser = $this->subuserRepository->findFirstWhere([ - ['user_id', '=', $user->id], - ['server_id', '=', $server->id], - ]); - - return $this->keyCreationService->handle($subuser->server_id, $subuser->user_id); - } - - if (! $updateIfExpired || Carbon::now()->diffInSeconds($key->expires_at, false) > 0) { - return $key->secret; - } - - return $this->keyUpdateService->handle($key->id); - } -} diff --git a/app/Services/DaemonKeys/DaemonKeyUpdateService.php b/app/Services/DaemonKeys/DaemonKeyUpdateService.php deleted file mode 100644 index 750d833d9..000000000 --- a/app/Services/DaemonKeys/DaemonKeyUpdateService.php +++ /dev/null @@ -1,88 +0,0 @@ -. - * - * 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. - */ - -namespace Pterodactyl\Services\DaemonKeys; - -use Carbon\Carbon; -use Webmozart\Assert\Assert; -use Illuminate\Contracts\Config\Repository as ConfigRepository; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; - -class DaemonKeyUpdateService -{ - /** - * @var \Carbon\Carbon - */ - protected $carbon; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface - */ - protected $repository; - - /** - * DaemonKeyUpdateService constructor. - * - * @param \Carbon\Carbon $carbon - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository - */ - public function __construct( - Carbon $carbon, - ConfigRepository $config, - DaemonKeyRepositoryInterface $repository - ) { - $this->carbon = $carbon; - $this->config = $config; - $this->repository = $repository; - } - - /** - * Update a daemon key to expire the previous one. - * - * @param int $key - * @return string - * - * @throws \RuntimeException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle($key) - { - Assert::integerish($key, 'First argument passed to handle must be an integer, received %s.'); - - $secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40); - $this->repository->withoutFreshModel()->update($key, [ - 'secret' => $secret, - 'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(), - ]); - - return $secret; - } -} diff --git a/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php b/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php deleted file mode 100644 index 7059be88e..000000000 --- a/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php +++ /dev/null @@ -1,89 +0,0 @@ -daemonRepository = $daemonRepository; - $this->repository = $repository; - } - - /** - * Grab all of the keys that exist for a single user and delete them from all - * daemon's that they are assigned to. If connection fails, this function will - * return an error. - * - * @param \Pterodactyl\Models\User $user - * @param bool $ignoreConnectionErrors - */ - public function handle(User $user, bool $ignoreConnectionErrors = false) - { - $keys = $this->repository->getKeysForRevocation($user); - - $keys->groupBy('node.id')->each(function ($group, $nodeId) use ($ignoreConnectionErrors) { - try { - $this->daemonRepository->setNode(collect($group)->first()->getRelation('node'))->revokeAccessKey(collect($group)->pluck('secret')->toArray()); - } catch (RequestException $exception) { - if (! $ignoreConnectionErrors) { - throw new DaemonConnectionException($exception); - } - - $this->setConnectionException($nodeId, $exception); - } - - $this->repository->deleteKeys(collect($group)->pluck('id')->toArray()); - }); - } - - /** - * Returns an array of exceptions that were returned by the handle function. - * - * @return RequestException[] - */ - public function getExceptions() - { - return $this->exceptions; - } - - /** - * Add an exception for a node to the array. - * - * @param int $node - * @param \GuzzleHttp\Exception\RequestException $exception - */ - protected function setConnectionException(int $node, RequestException $exception) - { - $this->exceptions[$node] = $exception; - } -} diff --git a/app/Services/Databases/DatabaseManagementService.php b/app/Services/Databases/DatabaseManagementService.php index 103befa55..ea58ba0c2 100644 --- a/app/Services/Databases/DatabaseManagementService.php +++ b/app/Services/Databases/DatabaseManagementService.php @@ -2,132 +2,174 @@ namespace Pterodactyl\Services\Databases; +use Exception; +use Pterodactyl\Models\Server; use Pterodactyl\Models\Database; use Pterodactyl\Helpers\Utilities; -use Illuminate\Database\DatabaseManager; +use Illuminate\Database\ConnectionInterface; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Extensions\DynamicDatabaseConnection; -use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; +use Pterodactyl\Repositories\Eloquent\DatabaseRepository; +use Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException; +use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException; +use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException; class DatabaseManagementService { /** - * @var \Illuminate\Database\DatabaseManager - */ - private $database; - - /** - * @var \Pterodactyl\Extensions\DynamicDatabaseConnection - */ - private $dynamic; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - private $repository; - - /** - * @var bool - */ - protected $useRandomHost = false; - - /** - * CreationService constructor. + * The regex used to validate that the database name passed through to the function is + * in the expected format. * - * @param \Illuminate\Database\DatabaseManager $database - * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter + * @see \Pterodactyl\Services\Databases\DatabaseManagementService::generateUniqueDatabaseName() */ + private const MATCH_NAME_REGEX = '/^(s[\d]+_)(.*)$/'; + + /** + * Determines if the service should validate the user's ability to create an additional + * database for this server. In almost all cases this should be true, but to keep things + * flexible you can also set it to false and create more databases than the server is + * allocated. + */ + protected bool $validateDatabaseLimit = true; + public function __construct( - DatabaseManager $database, - DynamicDatabaseConnection $dynamic, - DatabaseRepositoryInterface $repository, - Encrypter $encrypter + protected ConnectionInterface $connection, + protected DynamicDatabaseConnection $dynamic, + protected Encrypter $encrypter, + protected DatabaseRepository $repository ) { - $this->database = $database; - $this->dynamic = $dynamic; - $this->encrypter = $encrypter; - $this->repository = $repository; + } + + /** + * Generates a unique database name for the given server. This name should be passed through when + * calling this handle function for this service, otherwise the database will be created with + * whatever name is provided. + */ + public static function generateUniqueDatabaseName(string $name, int $serverId): string + { + // Max of 48 characters, including the s123_ that we append to the front. + return sprintf('s%d_%s', $serverId, substr($name, 0, 48 - strlen("s{$serverId}_"))); + } + + /** + * Set whether this class should validate that the server has enough slots + * left before creating the new database. + */ + public function setValidateDatabaseLimit(bool $validate): self + { + $this->validateDatabaseLimit = $validate; + + return $this; } /** * Create a new database that is linked to a specific host. * - * @param int $server - * @param array $data - * @return \Pterodactyl\Models\Database - * - * @throws \Exception + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException + * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException */ - public function create($server, array $data) + public function create(Server $server, array $data): Database { - $data['server_id'] = $server; - $data['database'] = sprintf('s%d_%s', $server, $data['database']); - $data['username'] = sprintf('u%d_%s', $server, str_random(10)); - $data['password'] = $this->encrypter->encrypt( - Utilities::randomStringWithSpecialCharacters(24) - ); + if (!config('pterodactyl.client_features.databases.enabled')) { + throw new DatabaseClientFeatureNotEnabledException(); + } + + if ($this->validateDatabaseLimit) { + // If the server has a limit assigned and we've already reached that limit, throw back + // an exception and kill the process. + if (!is_null($server->database_limit) && $server->databases()->count() >= $server->database_limit) { + throw new TooManyDatabasesException(); + } + } + + // Protect against developer mistakes... + if (empty($data['database']) || !preg_match(self::MATCH_NAME_REGEX, $data['database'])) { + throw new \InvalidArgumentException('The database name passed to DatabaseManagementService::handle MUST be prefixed with "s{server_id}_".'); + } + + $data = array_merge($data, [ + 'server_id' => $server->id, + 'username' => sprintf('u%d_%s', $server->id, str_random(10)), + 'password' => $this->encrypter->encrypt( + Utilities::randomStringWithSpecialCharacters(24) + ), + ]); + + $database = null; - $this->database->beginTransaction(); try { - $database = $this->repository->createIfNotExists($data); - $this->dynamic->set('dynamic', $data['database_host_id']); + return $this->connection->transaction(function () use ($data, &$database) { + $database = $this->createModel($data); - $this->repository->createDatabase($database->database); - $this->repository->createUser( - $database->username, - $database->remote, - $this->encrypter->decrypt($database->password) - ); - $this->repository->assignUserToDatabase( - $database->database, - $database->username, - $database->remote - ); - $this->repository->flush(); + $this->dynamic->set('dynamic', $data['database_host_id']); - $this->database->commit(); - } catch (\Exception $ex) { + $this->repository->createDatabase($database->database); + $this->repository->createUser( + $database->username, + $database->remote, + $this->encrypter->decrypt($database->password), + $database->max_connections + ); + $this->repository->assignUserToDatabase($database->database, $database->username, $database->remote); + $this->repository->flush(); + + return $database; + }); + } catch (\Exception $exception) { try { - if (isset($database) && $database instanceof Database) { + /** @var ?Database $database */ + if ($database instanceof Database) { $this->repository->dropDatabase($database->database); $this->repository->dropUser($database->username, $database->remote); $this->repository->flush(); } - } catch (\Exception $exTwo) { - // ignore an exception + } catch (\Exception $deletionException) { + // Do nothing here. We've already encountered an issue before this point so no + // reason to prioritize this error over the initial one. } - $this->database->rollBack(); - throw $ex; + throw $exception; } - - return $database; } /** * Delete a database from the given host server. * - * @param int $id - * @return bool|null - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Exception */ - public function delete($id) + public function delete(Database $database): ?bool { - $database = $this->repository->find($id); $this->dynamic->set('dynamic', $database->database_host_id); $this->repository->dropDatabase($database->database); $this->repository->dropUser($database->username, $database->remote); $this->repository->flush(); - return $this->repository->delete($id); + return $database->delete(); + } + + /** + * Create the database if there is not an identical match in the DB. While you can technically + * have the same name across multiple hosts, for the sake of keeping this logic easy to understand + * and avoiding user confusion we will ignore the specific host and just look across all hosts. + * + * @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException + * @throws \Throwable + */ + protected function createModel(array $data): Database + { + $exists = Database::query()->where('server_id', $data['server_id']) + ->where('database', $data['database']) + ->exists(); + + if ($exists) { + throw new DuplicateDatabaseNameException('A database with that name already exists for this server.'); + } + + $database = (new Database())->forceFill($data); + $database->saveOrFail(); + + return $database; } } diff --git a/app/Services/Databases/DatabasePasswordService.php b/app/Services/Databases/DatabasePasswordService.php index ad5882c49..3862b2393 100644 --- a/app/Services/Databases/DatabasePasswordService.php +++ b/app/Services/Databases/DatabasePasswordService.php @@ -11,55 +11,23 @@ use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; class DatabasePasswordService { - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Extensions\DynamicDatabaseConnection - */ - private $dynamic; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - private $repository; - /** * DatabasePasswordService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository - * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter */ public function __construct( - ConnectionInterface $connection, - DatabaseRepositoryInterface $repository, - DynamicDatabaseConnection $dynamic, - Encrypter $encrypter + private ConnectionInterface $connection, + private DynamicDatabaseConnection $dynamic, + private Encrypter $encrypter, + private DatabaseRepositoryInterface $repository ) { - $this->connection = $connection; - $this->dynamic = $dynamic; - $this->encrypter = $encrypter; - $this->repository = $repository; } /** * Updates a password for a given database. * - * @param \Pterodactyl\Models\Database|int $database - * @return string - * * @throws \Throwable */ - public function handle(Database $database): string + public function handle(Database|int $database): string { $password = Utilities::randomStringWithSpecialCharacters(24); @@ -71,7 +39,7 @@ class DatabasePasswordService ]); $this->repository->dropUser($database->username, $database->remote); - $this->repository->createUser($database->username, $database->remote, $password); + $this->repository->createUser($database->username, $database->remote, $password, $database->max_connections); $this->repository->assignUserToDatabase($database->database, $database->username, $database->remote); $this->repository->flush(); }); diff --git a/app/Services/Databases/DeployServerDatabaseService.php b/app/Services/Databases/DeployServerDatabaseService.php index c8b5ed179..f10473bf1 100644 --- a/app/Services/Databases/DeployServerDatabaseService.php +++ b/app/Services/Databases/DeployServerDatabaseService.php @@ -2,89 +2,51 @@ namespace Pterodactyl\Services\Databases; +use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Pterodactyl\Models\Database; -use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; -use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; -use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException; +use Pterodactyl\Models\DatabaseHost; use Pterodactyl\Exceptions\Service\Database\NoSuitableDatabaseHostException; -use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException; class DeployServerDatabaseService { /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface + * DeployServerDatabaseService constructor. */ - private $databaseHostRepository; - - /** - * @var \Pterodactyl\Services\Databases\DatabaseManagementService - */ - private $managementService; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - private $repository; - - /** - * ServerDatabaseCreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $databaseHostRepository - * @param \Pterodactyl\Services\Databases\DatabaseManagementService $managementService - */ - public function __construct( - DatabaseRepositoryInterface $repository, - DatabaseHostRepositoryInterface $databaseHostRepository, - DatabaseManagementService $managementService - ) { - $this->databaseHostRepository = $databaseHostRepository; - $this->managementService = $managementService; - $this->repository = $repository; + public function __construct(private DatabaseManagementService $managementService) + { } /** - * @param \Pterodactyl\Models\Server $server - * @param array $data - * @return \Pterodactyl\Models\Database - * + * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException - * @throws \Exception */ public function handle(Server $server, array $data): Database { - if (! config('pterodactyl.client_features.databases.enabled')) { - throw new DatabaseClientFeatureNotEnabledException; - } + Assert::notEmpty($data['database'] ?? null); + Assert::notEmpty($data['remote'] ?? null); - $databases = $this->repository->findCountWhere([['server_id', '=', $server->id]]); - if (! is_null($server->database_limit) && $databases >= $server->database_limit) { - throw new TooManyDatabasesException; - } - - $allowRandom = config('pterodactyl.client_features.databases.allow_random'); - $hosts = $this->databaseHostRepository->setColumns(['id'])->findWhere([ - ['node_id', '=', $server->node_id], - ]); - - if ($hosts->isEmpty() && ! $allowRandom) { - throw new NoSuitableDatabaseHostException; - } - - if ($hosts->isEmpty()) { - $hosts = $this->databaseHostRepository->setColumns(['id'])->all(); - if ($hosts->isEmpty()) { - throw new NoSuitableDatabaseHostException; + $databaseHostId = $server->node->database_host_id; + if (is_null($databaseHostId)) { + if (!config('pterodactyl.client_features.databases.allow_random')) { + throw new NoSuitableDatabaseHostException(); } + + $hosts = DatabaseHost::query()->get()->toBase(); + if ($hosts->isEmpty()) { + throw new NoSuitableDatabaseHostException(); + } + + /** @var \Pterodactyl\Models\DatabaseHost $databaseHost */ + $databaseHost = $hosts->random(); + $databaseHostId = $databaseHost->id; } - $host = $hosts->random(); - - return $this->managementService->create($server->id, [ - 'database_host_id' => $host->id, - 'database' => array_get($data, 'database'), - 'remote' => array_get($data, 'remote'), + return $this->managementService->create($server, [ + 'database_host_id' => $databaseHostId, + 'database' => DatabaseManagementService::generateUniqueDatabaseName($data['database'], $server->id), + 'remote' => $data['remote'], ]); } } diff --git a/app/Services/Databases/Hosts/HostCreationService.php b/app/Services/Databases/Hosts/HostCreationService.php index d6ea2f485..d33a15856 100644 --- a/app/Services/Databases/Hosts/HostCreationService.php +++ b/app/Services/Databases/Hosts/HostCreationService.php @@ -11,60 +11,21 @@ use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; class HostCreationService { - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Illuminate\Database\DatabaseManager - */ - private $databaseManager; - - /** - * @var \Pterodactyl\Extensions\DynamicDatabaseConnection - */ - private $dynamic; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface - */ - private $repository; - /** * HostCreationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Illuminate\Database\DatabaseManager $databaseManager - * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository - * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter */ public function __construct( - ConnectionInterface $connection, - DatabaseManager $databaseManager, - DatabaseHostRepositoryInterface $repository, - DynamicDatabaseConnection $dynamic, - Encrypter $encrypter + private ConnectionInterface $connection, + private DatabaseManager $databaseManager, + private DynamicDatabaseConnection $dynamic, + private Encrypter $encrypter, + private DatabaseHostRepositoryInterface $repository ) { - $this->connection = $connection; - $this->databaseManager = $databaseManager; - $this->dynamic = $dynamic; - $this->encrypter = $encrypter; - $this->repository = $repository; } /** * Create a new database host on the Panel. * - * @param array $data - * @return \Pterodactyl\Models\DatabaseHost - * * @throws \Throwable */ public function handle(array $data): DatabaseHost diff --git a/app/Services/Databases/Hosts/HostDeletionService.php b/app/Services/Databases/Hosts/HostDeletionService.php index b69c8dcf9..4a06af8da 100644 --- a/app/Services/Databases/Hosts/HostDeletionService.php +++ b/app/Services/Databases/Hosts/HostDeletionService.php @@ -8,37 +8,19 @@ use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; class HostDeletionService { - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - private $databaseRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface - */ - private $repository; - /** * HostDeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository - * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository */ public function __construct( - DatabaseRepositoryInterface $databaseRepository, - DatabaseHostRepositoryInterface $repository + private DatabaseRepositoryInterface $databaseRepository, + private DatabaseHostRepositoryInterface $repository ) { - $this->databaseRepository = $databaseRepository; - $this->repository = $repository; } /** * Delete a specified host from the Panel if no databases are * attached to it. * - * @param int $host - * @return int - * * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException */ public function handle(int $host): int diff --git a/app/Services/Databases/Hosts/HostUpdateService.php b/app/Services/Databases/Hosts/HostUpdateService.php index cb6312d21..363deb1b3 100644 --- a/app/Services/Databases/Hosts/HostUpdateService.php +++ b/app/Services/Databases/Hosts/HostUpdateService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Databases\Hosts; @@ -19,65 +12,25 @@ use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; class HostUpdateService { /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Illuminate\Database\DatabaseManager - */ - private $databaseManager; - - /** - * @var \Pterodactyl\Extensions\DynamicDatabaseConnection - */ - private $dynamic; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface - */ - private $repository; - - /** - * DatabaseHostService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Illuminate\Database\DatabaseManager $databaseManager - * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository - * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter + * HostUpdateService constructor. */ public function __construct( - ConnectionInterface $connection, - DatabaseManager $databaseManager, - DatabaseHostRepositoryInterface $repository, - DynamicDatabaseConnection $dynamic, - Encrypter $encrypter + private ConnectionInterface $connection, + private DatabaseManager $databaseManager, + private DynamicDatabaseConnection $dynamic, + private Encrypter $encrypter, + private DatabaseHostRepositoryInterface $repository ) { - $this->connection = $connection; - $this->databaseManager = $databaseManager; - $this->dynamic = $dynamic; - $this->encrypter = $encrypter; - $this->repository = $repository; } /** * Update a database host and persist to the database. * - * @param int $hostId - * @param array $data - * @return \Pterodactyl\Models\DatabaseHost - * * @throws \Throwable */ public function handle(int $hostId, array $data): DatabaseHost { - if (! empty(array_get($data, 'password'))) { + if (!empty(array_get($data, 'password'))) { $data['password'] = $this->encrypter->encrypt($data['password']); } else { unset($data['password']); diff --git a/app/Services/Deployment/AllocationSelectionService.php b/app/Services/Deployment/AllocationSelectionService.php index 633ba1f5e..e14b11a17 100644 --- a/app/Services/Deployment/AllocationSelectionService.php +++ b/app/Services/Deployment/AllocationSelectionService.php @@ -10,45 +10,25 @@ use Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException; class AllocationSelectionService { - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - private $repository; + protected bool $dedicated = false; - /** - * @var bool - */ - protected $dedicated = false; + protected array $nodes = []; - /** - * @var array - */ - protected $nodes = []; - - /** - * @var array - */ - protected $ports = []; + protected array $ports = []; /** * AllocationSelectionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository */ - public function __construct(AllocationRepositoryInterface $repository) + public function __construct(private AllocationRepositoryInterface $repository) { - $this->repository = $repository; } /** * Toggle if the selected allocation should be the only allocation belonging * to the given IP address. If true an allocation will not be selected if an IP * already has another server set to use on if its allocations. - * - * @param bool $dedicated - * @return $this */ - public function setDedicated(bool $dedicated) + public function setDedicated(bool $dedicated): self { $this->dedicated = $dedicated; @@ -58,11 +38,8 @@ class AllocationSelectionService /** * A list of node IDs that should be used when selecting an allocation. If empty, all * nodes will be used to filter with. - * - * @param array $nodes - * @return $this */ - public function setNodes(array $nodes) + public function setNodes(array $nodes): self { $this->nodes = $nodes; @@ -74,12 +51,9 @@ class AllocationSelectionService * empty, all ports will be considered when finding an allocation. If set, only ports appearing * in the array or range will be used. * - * @param array $ports - * @return $this - * * @throws \Pterodactyl\Exceptions\DisplayException */ - public function setPorts(array $ports) + public function setPorts(array $ports): self { $stored = []; foreach ($ports as $port) { @@ -90,7 +64,7 @@ class AllocationSelectionService // Ranges are stored in the ports array as an array which can be // better processed in the repository. if (preg_match(AssignmentService::PORT_RANGE_REGEX, $port, $matches)) { - if (abs($matches[2] - $matches[1]) > AssignmentService::PORT_RANGE_LIMIT) { + if (abs(intval($matches[2]) - intval($matches[1])) > AssignmentService::PORT_RANGE_LIMIT) { throw new DisplayException(trans('exceptions.allocations.too_many_ports')); } @@ -106,8 +80,6 @@ class AllocationSelectionService /** * Return a single allocation that should be used as the default allocation for a server. * - * @return \Pterodactyl\Models\Allocation - * * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException */ public function handle(): Allocation diff --git a/app/Services/Deployment/FindViableNodesService.php b/app/Services/Deployment/FindViableNodesService.php index 6d6832c27..bd2acfdd2 100644 --- a/app/Services/Deployment/FindViableNodesService.php +++ b/app/Services/Deployment/FindViableNodesService.php @@ -2,50 +2,25 @@ namespace Pterodactyl\Services\Deployment; +use Pterodactyl\Models\Node; use Webmozart\Assert\Assert; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; +use Illuminate\Support\Collection; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException; class FindViableNodesService { - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - private $repository; - - /** - * @var array - */ - protected $locations = []; - - /** - * @var int - */ - protected $disk; - - /** - * @var int - */ - protected $memory; - - /** - * FindViableNodesService constructor. - * - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository - */ - public function __construct(NodeRepositoryInterface $repository) - { - $this->repository = $repository; - } + protected array $locations = []; + protected ?int $disk = null; + protected ?int $memory = null; /** * Set the locations that should be searched through to locate available nodes. - * - * @param array $locations - * @return $this */ public function setLocations(array $locations): self { + Assert::allIntegerish($locations, 'An array of location IDs should be provided when calling setLocations.'); + $this->locations = $locations; return $this; @@ -55,9 +30,6 @@ class FindViableNodesService * Set the amount of disk that will be used by the server being created. Nodes will be * filtered out if they do not have enough available free disk space for this server * to be placed on. - * - * @param int $disk - * @return $this */ public function setDisk(int $disk): self { @@ -69,9 +41,6 @@ class FindViableNodesService /** * Set the amount of memory that this server will be using. As with disk space, nodes that * do not have enough free memory will be filtered out. - * - * @param int $memory - * @return $this */ public function setMemory(int $memory): self { @@ -90,32 +59,42 @@ class FindViableNodesService * are tossed out, as are any nodes marked as non-public, meaning automatic * deployments should not be done against them. * - * @return int[] + * @param int|null $page If provided the results will be paginated by returning + * up to 50 nodes at a time starting at the provided page. + * If "null" is provided as the value no pagination will + * be used. + * * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException */ - public function handle(): array + public function handle(int $perPage = null, int $page = null): LengthAwarePaginator|Collection { - Assert::integer($this->disk, 'Calls to ' . __METHOD__ . ' must have the disk space set as an integer, received %s'); - Assert::integer($this->memory, 'Calls to ' . __METHOD__ . ' must have the memory usage set as an integer, received %s'); + Assert::integer($this->disk, 'Disk space must be an int, got %s'); + Assert::integer($this->memory, 'Memory usage must be an int, got %s'); - $nodes = $this->repository->getNodesWithResourceUse($this->locations, $this->disk, $this->memory); - $viable = []; + $query = Node::query()->select('nodes.*') + ->selectRaw('COALESCE(SUM(servers.memory), 0) as sum_memory') + ->selectRaw('COALESCE(SUM(servers.disk), 0) as sum_disk') + ->leftJoin('servers', 'servers.node_id', '=', 'nodes.id') + ->where('nodes.public', 1); - foreach ($nodes as $node) { - $memoryLimit = $node->memory * (1 + ($node->memory_overallocate / 100)); - $diskLimit = $node->disk * (1 + ($node->disk_overallocate / 100)); - - if (($node->sum_memory + $this->memory) > $memoryLimit || ($node->sum_disk + $this->disk) > $diskLimit) { - continue; - } - - $viable[] = $node->id; + if (!empty($this->locations)) { + $query = $query->whereIn('location_id', $this->locations); } - if (empty($viable)) { + $results = $query->groupBy('nodes.id') + ->havingRaw('(COALESCE(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1.0 + (nodes.memory_overallocate / 100.0)))', [$this->memory]) + ->havingRaw('(COALESCE(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1.0 + (nodes.disk_overallocate / 100.0)))', [$this->disk]); + + if (!is_null($page)) { + $results = $results->paginate($perPage ?? 50, ['*'], 'page', $page); + } else { + $results = $results->get()->toBase(); + } + + if ($results->isEmpty()) { throw new NoViableNodeException(trans('exceptions.deployment.no_viable_nodes')); } - return $viable; + return $results; } } diff --git a/app/Services/Eggs/EggConfigurationService.php b/app/Services/Eggs/EggConfigurationService.php index a73e3f6a8..4a61c2ea6 100644 --- a/app/Services/Eggs/EggConfigurationService.php +++ b/app/Services/Eggs/EggConfigurationService.php @@ -1,54 +1,245 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs; -use Pterodactyl\Models\Egg; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; +use Pterodactyl\Models\Server; +use Pterodactyl\Services\Servers\ServerConfigurationStructureService; class EggConfigurationService { - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - /** * EggConfigurationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository */ - public function __construct(EggRepositoryInterface $repository) + public function __construct(private ServerConfigurationStructureService $configurationStructureService) { - $this->repository = $repository; } /** * Return an Egg file to be used by the Daemon. - * - * @param int|\Pterodactyl\Models\Egg $egg - * @return array - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function handle($egg): array + public function handle(Server $server): array { - if (! $egg instanceof Egg) { - $egg = $this->repository->getWithCopyAttributes($egg); + $configs = $this->replacePlaceholders( + $server, + json_decode($server->egg->inherit_config_files) + ); + + return [ + 'startup' => $this->convertStartupToNewFormat(json_decode($server->egg->inherit_config_startup, true)), + 'stop' => $this->convertStopToNewFormat($server->egg->inherit_config_stop), + 'configs' => $configs, + ]; + } + + /** + * Convert the "done" variable into an array if it is not currently one. + */ + protected function convertStartupToNewFormat(array $startup): array + { + $done = Arr::get($startup, 'done'); + + return [ + 'done' => is_string($done) ? [$done] : $done, + 'user_interaction' => [], + 'strip_ansi' => Arr::get($startup, 'strip_ansi') ?? false, + ]; + } + + /** + * Converts a legacy stop string into a new generation stop option for a server. + * + * For most eggs, this ends up just being a command sent to the server console, but + * if the stop command is something starting with a caret (^), it will be converted + * into the associated kill signal for the instance. + */ + protected function convertStopToNewFormat(string $stop): array + { + if (!Str::startsWith($stop, '^')) { + return [ + 'type' => 'command', + 'value' => $stop, + ]; + } + + $signal = substr($stop, 1); + if (strtoupper($signal) === 'C') { + return [ + 'type' => 'stop', + 'value' => null, + ]; } return [ - 'startup' => json_decode($egg->inherit_config_startup), - 'stop' => $egg->inherit_config_stop, - 'configs' => json_decode($egg->inherit_config_files), - 'log' => json_decode($egg->inherit_config_logs), - 'query' => 'none', + 'type' => 'signal', + 'value' => strtoupper($signal), ]; } + + protected function replacePlaceholders(Server $server, object $configs): array + { + // Get the legacy configuration structure for the server so that we + // can property map the egg placeholders to values. + $structure = $this->configurationStructureService->handle($server, [], true); + + $response = []; + // Normalize the output of the configuration for the new Wings Daemon to more + // easily ingest, as well as make things more flexible down the road. + foreach ($configs as $file => $data) { + // Try to head off any errors relating to parsing a set of configuration files + // or other JSON data for the egg. This should probably be blocked at the time + // of egg creation/update, but it isn't so this check will at least prevent a + // 500 error which would crash the entire Wings boot process. + // + // @see https://github.com/pterodactyl/panel/issues/3055 + if (!is_object($data) || !isset($data->find)) { + continue; + } + + $append = array_merge((array) $data, ['file' => $file, 'replace' => []]); + + foreach ($this->iterate($data->find, $structure) as $find => $replace) { + if (is_object($replace)) { + foreach ($replace as $match => $replaceWith) { + $append['replace'][] = [ + 'match' => $find, + 'if_value' => $match, + 'replace_with' => $replaceWith, + ]; + } + + continue; + } + + $append['replace'][] = [ + 'match' => $find, + 'replace_with' => $replace, + ]; + } + + unset($append['find']); + + $response[] = $append; + } + + return $response; + } + + /** + * Replaces the legacy modifies from eggs with their new counterpart. The legacy Daemon would + * set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their respective values on the Daemon + * side. Ensure that anything referencing those properly replaces them with the matching config + * value. + */ + protected function replaceLegacyModifiers(string $key, string $value): string + { + switch ($key) { + case 'config.docker.interface': + $replace = 'config.docker.network.interface'; + break; + case 'server.build.env.SERVER_MEMORY': + case 'env.SERVER_MEMORY': + $replace = 'server.build.memory'; + break; + case 'server.build.env.SERVER_IP': + case 'env.SERVER_IP': + $replace = 'server.build.default.ip'; + break; + case 'server.build.env.SERVER_PORT': + case 'env.SERVER_PORT': + $replace = 'server.build.default.port'; + break; + default: + // By default, we don't need to change anything, only if we ended up matching a specific legacy item. + $replace = $key; + } + + return str_replace("{{{$key}}}", "{{{$replace}}}", $value); + } + + protected function matchAndReplaceKeys(mixed $value, array $structure): mixed + { + preg_match_all('/{{(?[\w.-]*)}}/', $value, $matches); + + foreach ($matches['key'] as $key) { + // Matched something in {{server.X}} format, now replace that with the actual + // value from the server properties. + // + // The Daemon supports server.X, env.X, and config.X placeholders. + if (!Str::startsWith($key, ['server.', 'env.', 'config.'])) { + continue; + } + + // Don't do a replacement on anything that is not a string, we don't want to unintentionally + // modify the resulting output. + if (!is_string($value)) { + continue; + } + + $value = $this->replaceLegacyModifiers($key, $value); + + // We don't want to do anything with config keys since the Daemon will need to handle + // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker + // interface to proxy through, but the Panel would be unaware of that. + if (Str::startsWith($key, 'config.')) { + continue; + } + + // Replace anything starting with "server." with the value out of the server configuration + // array that used to be created for the old daemon. + if (Str::startsWith($key, 'server.')) { + $plucked = Arr::get($structure, preg_replace('/^server\./', '', $key), ''); + + $value = str_replace("{{{$key}}}", $plucked, $value); + continue; + } + + // Finally, replace anything starting with env. with the expected environment + // variable from the server configuration. + $plucked = Arr::get( + $structure, + preg_replace('/^env\./', 'build.env.', $key), + '' + ); + + $value = str_replace("{{{$key}}}", $plucked, $value); + } + + return $value; + } + + /** + * Iterates over a set of "find" values for a given file in the parser configuration. If + * the value of the line match is something iterable, continue iterating, otherwise perform + * a match & replace. + */ + private function iterate(mixed $data, array $structure): mixed + { + if (!is_iterable($data) && !is_object($data)) { + return $data; + } + + // Remember, in PHP objects are always passed by reference, so if we do not clone this object + // instance we'll end up making modifications to the object outside the scope of this function + // which leads to some fun behavior in the parser. + if (is_array($data)) { + // Copy the array. + // NOTE: if the array contains any objects, they will be passed by reference. + $clone = $data; + } else { + $clone = clone $data; + } + foreach ($clone as $key => &$value) { + if (is_iterable($value) || is_object($value)) { + $value = $this->iterate($value, $structure); + + continue; + } + + $value = $this->matchAndReplaceKeys($value, $structure); + } + + return $clone; + } } diff --git a/app/Services/Eggs/EggCreationService.php b/app/Services/Eggs/EggCreationService.php index 45fa8895f..b084b0cca 100644 --- a/app/Services/Eggs/EggCreationService.php +++ b/app/Services/Eggs/EggCreationService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs; @@ -18,41 +11,23 @@ use Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException; // When a mommy and a daddy pterodactyl really like each other... class EggCreationService { - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - /** * EggCreationService constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository */ - public function __construct(ConfigRepository $config, EggRepositoryInterface $repository) + public function __construct(private ConfigRepository $config, private EggRepositoryInterface $repository) { - $this->config = $config; - $this->repository = $repository; } /** * Create a new service option and assign it to the given service. * - * @param array $data - * @return \Pterodactyl\Models\Egg - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException */ public function handle(array $data): Egg { $data['config_from'] = array_get($data, 'config_from'); - if (! is_null($data['config_from'])) { + if (!is_null($data['config_from'])) { $results = $this->repository->findCountWhere([ ['nest_id', '=', array_get($data, 'nest_id')], ['id', '=', array_get($data, 'config_from')], diff --git a/app/Services/Eggs/EggDeletionService.php b/app/Services/Eggs/EggDeletionService.php index 5179f6a50..7e4013351 100644 --- a/app/Services/Eggs/EggDeletionService.php +++ b/app/Services/Eggs/EggDeletionService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs; @@ -16,36 +9,18 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class EggDeletionService { - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - /** * EggDeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository */ public function __construct( - ServerRepositoryInterface $serverRepository, - EggRepositoryInterface $repository + protected ServerRepositoryInterface $serverRepository, + protected EggRepositoryInterface $repository ) { - $this->repository = $repository; - $this->serverRepository = $serverRepository; } /** * Delete an Egg from the database if it has no active servers attached to it. * - * @param int $egg - * @return int - * * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException * @throws \Pterodactyl\Exceptions\Service\Egg\HasChildrenException */ diff --git a/app/Services/Eggs/EggParserService.php b/app/Services/Eggs/EggParserService.php new file mode 100644 index 000000000..0890c3963 --- /dev/null +++ b/app/Services/Eggs/EggParserService.php @@ -0,0 +1,81 @@ +convertToV2($parsed); + } + + /** + * Fills the provided model with the parsed JSON data. + */ + public function fillFromParsed(Egg $model, array $parsed): Egg + { + return $model->forceFill([ + 'name' => Arr::get($parsed, 'name'), + 'description' => Arr::get($parsed, 'description'), + 'features' => Arr::get($parsed, 'features'), + 'docker_images' => Arr::get($parsed, 'docker_images'), + 'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist')) + ->filter(fn ($value) => !empty($value)), + 'update_url' => Arr::get($parsed, 'meta.update_url'), + 'config_files' => Arr::get($parsed, 'config.files'), + 'config_startup' => Arr::get($parsed, 'config.startup'), + 'config_stop' => Arr::get($parsed, 'config.stop'), + 'startup' => Arr::get($parsed, 'startup'), + 'script_install' => Arr::get($parsed, 'scripts.installation.script'), + 'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'), + 'script_container' => Arr::get($parsed, 'scripts.installation.container'), + ]); + } + + /** + * Converts a PTDL_V1 egg into the expected PTDL_V2 egg format. This just handles + * the "docker_images" field potentially not being present, and not being in the + * expected "key => value" format. + */ + protected function convertToV2(array $parsed): array + { + if (Arr::get($parsed, 'meta.version') === Egg::EXPORT_VERSION) { + return $parsed; + } + + // Maintain backwards compatability for eggs that are still using the old single image + // string format. New eggs can provide an array of Docker images that can be used. + if (!isset($parsed['images'])) { + $images = [Arr::get($parsed, 'image') ?? 'nil']; + } else { + $images = $parsed['images']; + } + + unset($parsed['images'], $parsed['image']); + + $parsed['docker_images'] = []; + foreach ($images as $image) { + $parsed['docker_images'][$image] = $image; + } + + $parsed['variables'] = array_map(function ($value) { + return array_merge($value, ['field_type' => 'text']); + }, $parsed['variables']); + + return $parsed; + } +} diff --git a/app/Services/Eggs/EggUpdateService.php b/app/Services/Eggs/EggUpdateService.php index 14d655178..7ca442ba3 100644 --- a/app/Services/Eggs/EggUpdateService.php +++ b/app/Services/Eggs/EggUpdateService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs; @@ -15,38 +8,23 @@ use Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException; class EggUpdateService { - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - /** * EggUpdateService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository */ - public function __construct(EggRepositoryInterface $repository) + public function __construct(protected EggRepositoryInterface $repository) { - $this->repository = $repository; } /** * Update a service option. * - * @param int|\Pterodactyl\Models\Egg $egg - * @param array $data - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException */ - public function handle($egg, array $data) + public function handle(Egg $egg, array $data): void { - if (! $egg instanceof Egg) { - $egg = $this->repository->find($egg); - } - - if (! is_null(array_get($data, 'config_from'))) { + if (!is_null(array_get($data, 'config_from'))) { $results = $this->repository->findCountWhere([ ['nest_id', '=', $egg->nest_id], ['id', '=', array_get($data, 'config_from')], @@ -57,6 +35,10 @@ class EggUpdateService } } + // TODO(dane): Once the admin UI is done being reworked and this is exposed + // in said UI, remove this so that you can actually update the denylist. + unset($data['file_denylist']); + $this->repository->withoutFreshModel()->update($egg->id, $data); } } diff --git a/app/Services/Eggs/Scripts/InstallScriptService.php b/app/Services/Eggs/Scripts/InstallScriptService.php index b71c10d87..334157236 100644 --- a/app/Services/Eggs/Scripts/InstallScriptService.php +++ b/app/Services/Eggs/Scripts/InstallScriptService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs\Scripts; @@ -15,39 +8,24 @@ use Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException; class InstallScriptService { - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - /** * InstallScriptService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository */ - public function __construct(EggRepositoryInterface $repository) + public function __construct(protected EggRepositoryInterface $repository) { - $this->repository = $repository; } /** * Modify the install script for a given Egg. * - * @param int|\Pterodactyl\Models\Egg $egg - * @param array $data - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException */ - public function handle($egg, array $data) + public function handle(Egg $egg, array $data): void { - if (! $egg instanceof Egg) { - $egg = $this->repository->find($egg); - } - - if (! is_null(array_get($data, 'copy_script_from'))) { - if (! $this->repository->isCopyableScript(array_get($data, 'copy_script_from'), $egg->nest_id)) { + if (!is_null(array_get($data, 'copy_script_from'))) { + if (!$this->repository->isCopyableScript(array_get($data, 'copy_script_from'), $egg->nest_id)) { throw new InvalidCopyFromException(trans('exceptions.nest.egg.invalid_copy_id')); } } diff --git a/app/Services/Eggs/Sharing/EggExporterService.php b/app/Services/Eggs/Sharing/EggExporterService.php index 25a7131fa..d52fe7371 100644 --- a/app/Services/Eggs/Sharing/EggExporterService.php +++ b/app/Services/Eggs/Sharing/EggExporterService.php @@ -1,40 +1,25 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs\Sharing; use Carbon\Carbon; +use Pterodactyl\Models\Egg; +use Illuminate\Support\Collection; +use Pterodactyl\Models\EggVariable; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; class EggExporterService { - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - /** * EggExporterService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository */ - public function __construct(EggRepositoryInterface $repository) + public function __construct(protected EggRepositoryInterface $repository) { - $this->repository = $repository; } /** * Return a JSON representation of an egg and its variables. * - * @param int $egg - * @return string - * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function handle(int $egg): string @@ -44,18 +29,22 @@ class EggExporterService $struct = [ '_comment' => 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO', 'meta' => [ - 'version' => 'PTDL_v1', + 'version' => Egg::EXPORT_VERSION, + 'update_url' => $egg->update_url, ], - 'exported_at' => Carbon::now()->toIso8601String(), + 'exported_at' => Carbon::now()->toAtomString(), 'name' => $egg->name, 'author' => $egg->author, 'description' => $egg->description, - 'image' => $egg->docker_image, + 'features' => $egg->features, + 'docker_images' => $egg->docker_images, + 'file_denylist' => Collection::make($egg->inherit_file_denylist)->filter(function ($value) { + return !empty($value); + }), 'startup' => $egg->startup, 'config' => [ 'files' => $egg->inherit_config_files, 'startup' => $egg->inherit_config_startup, - 'logs' => $egg->inherit_config_logs, 'stop' => $egg->inherit_config_stop, ], 'scripts' => [ @@ -65,10 +54,11 @@ class EggExporterService 'entrypoint' => $egg->copy_script_entry, ], ], - 'variables' => $egg->variables->transform(function ($item) { - return collect($item->toArray())->except([ - 'id', 'egg_id', 'created_at', 'updated_at', - ])->toArray(); + 'variables' => $egg->variables->transform(function (EggVariable $item) { + return Collection::make($item->toArray()) + ->except(['id', 'egg_id', 'created_at', 'updated_at']) + ->merge(['field_type' => 'text']) + ->toArray(); }), ]; diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index d4e048f9a..57dd5c8c9 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -1,124 +1,129 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs\Sharing; use Ramsey\Uuid\Uuid; +use Illuminate\Support\Arr; use Pterodactyl\Models\Egg; +use Pterodactyl\Models\Nest; +use Symfony\Component\Yaml\Yaml; use Illuminate\Http\UploadedFile; +use Pterodactyl\Models\EggVariable; use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Contracts\Repository\NestRepositoryInterface; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Services\Eggs\EggParserService; +use Symfony\Component\Yaml\Exception\ParseException; use Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException; +use Pterodactyl\Exceptions\Service\Egg\BadYamlFormatException; use Pterodactyl\Exceptions\Service\InvalidFileUploadException; -use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; class EggImporterService { - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface - */ - protected $eggVariableRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - protected $nestRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - - /** - * EggImporterService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $eggVariableRepository - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $nestRepository - */ public function __construct( - ConnectionInterface $connection, - EggRepositoryInterface $repository, - EggVariableRepositoryInterface $eggVariableRepository, - NestRepositoryInterface $nestRepository + private ConnectionInterface $connection, + private EggParserService $eggParserService ) { - $this->connection = $connection; - $this->eggVariableRepository = $eggVariableRepository; - $this->repository = $repository; - $this->nestRepository = $nestRepository; } /** * Take an uploaded JSON file and parse it into a new egg. * - * @param \Illuminate\Http\UploadedFile $file - * @param int $nest - * @return \Pterodactyl\Models\Egg + * @deprecated use `handleFile` or `handleContent` instead * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadYamlFormatException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\DisplayException */ - public function handle(UploadedFile $file, int $nest): Egg + public function handle(UploadedFile $file, int $nestId): Egg { - if ($file->getError() !== UPLOAD_ERR_OK || ! $file->isFile()) { - throw new InvalidFileUploadException(trans('exceptions.nest.importer.file_error')); + return $this->handleFile($nestId, $file); + } + + /** + * ? + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadYamlFormatException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function handleFile(int $nestId, UploadedFile $file): Egg + { + if ($file->getError() !== UPLOAD_ERR_OK || !$file->isFile()) { + throw new InvalidFileUploadException(sprintf('The selected file ["%s"] was not in a valid format to import. (is_file: %s is_valid: %s err_code: %s err: %s)', $file->getFilename(), $file->isFile() ? 'true' : 'false', $file->isValid() ? 'true' : 'false', $file->getError(), $file->getErrorMessage())); } - $parsed = json_decode($file->openFile()->fread($file->getSize())); - if (json_last_error() !== 0) { - throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', [ - 'error' => json_last_error_msg(), - ])); + return $this->handleContent($nestId, $file->openFile()->fread($file->getSize()), 'application/json'); + } + + /** + * ? + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadYamlFormatException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function handleContent(int $nestId, string $content, string $contentType): Egg + { + switch (true) { + case str_starts_with($contentType, 'application/json'): + $parsed = json_decode($content, true); + if (json_last_error() !== 0) { + throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', ['error' => json_last_error_msg()])); + } + + return $this->handleArray($nestId, $parsed); + case str_starts_with($contentType, 'application/yaml'): + try { + $parsed = Yaml::parse($content); + + return $this->handleArray($nestId, $parsed); + } catch (ParseException $exception) { + throw new BadYamlFormatException('There was an error while attempting to parse the YAML: ' . $exception->getMessage() . '.'); + } + default: + throw new DisplayException('unknown content type'); } + } - if (object_get($parsed, 'meta.version') !== 'PTDL_v1') { - throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided')); - } + /** + * ? + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + */ + private function handleArray(int $nestId, array $parsed): Egg + { + $parsed = $this->eggParserService->handle($parsed); - $nest = $this->nestRepository->getWithEggs($nest); - $this->connection->beginTransaction(); + /** @var \Pterodactyl\Models\Nest $nest */ + $nest = Nest::query()->with('eggs', 'eggs.variables')->findOrFail($nestId); - $egg = $this->repository->create([ - 'uuid' => Uuid::uuid4()->toString(), - 'nest_id' => $nest->id, - 'author' => object_get($parsed, 'author'), - 'name' => object_get($parsed, 'name'), - 'description' => object_get($parsed, 'description'), - 'docker_image' => object_get($parsed, 'image'), - 'config_files' => object_get($parsed, 'config.files'), - 'config_startup' => object_get($parsed, 'config.startup'), - 'config_logs' => object_get($parsed, 'config.logs'), - 'config_stop' => object_get($parsed, 'config.stop'), - 'startup' => object_get($parsed, 'startup'), - 'script_install' => object_get($parsed, 'scripts.installation.script'), - 'script_entry' => object_get($parsed, 'scripts.installation.entrypoint'), - 'script_container' => object_get($parsed, 'scripts.installation.container'), - 'copy_script_from' => null, - ], true, true); + return $this->connection->transaction(function () use ($nest, $parsed) { + $egg = (new Egg())->forceFill([ + 'uuid' => Uuid::uuid4()->toString(), + 'nest_id' => $nest->id, + 'author' => Arr::get($parsed, 'author'), + 'copy_script_from' => null, + ]); - collect($parsed->variables)->each(function ($variable) use ($egg) { - $this->eggVariableRepository->create(array_merge((array) $variable, [ - 'egg_id' => $egg->id, - ])); + $egg = $this->eggParserService->fillFromParsed($egg, $parsed); + $egg->save(); + + foreach ($parsed['variables'] ?? [] as $variable) { + EggVariable::query()->forceCreate(array_merge($variable, ['egg_id' => $egg->id])); + } + + return $egg; }); - - $this->connection->commit(); - - return $egg; } } diff --git a/app/Services/Eggs/Sharing/EggUpdateImporterService.php b/app/Services/Eggs/Sharing/EggUpdateImporterService.php index 50342667d..d428ce8f4 100644 --- a/app/Services/Eggs/Sharing/EggUpdateImporterService.php +++ b/app/Services/Eggs/Sharing/EggUpdateImporterService.php @@ -2,112 +2,61 @@ namespace Pterodactyl\Services\Eggs\Sharing; +use Pterodactyl\Models\Egg; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Collection; +use Pterodactyl\Models\EggVariable; use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; +use Pterodactyl\Services\Eggs\EggParserService; use Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException; use Pterodactyl\Exceptions\Service\InvalidFileUploadException; -use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; class EggUpdateImporterService { - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface - */ - protected $variableRepository; - /** * EggUpdateImporterService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $variableRepository */ public function __construct( - ConnectionInterface $connection, - EggRepositoryInterface $repository, - EggVariableRepositoryInterface $variableRepository + private ConnectionInterface $connection, + private EggParserService $eggParserService ) { - $this->connection = $connection; - $this->repository = $repository; - $this->variableRepository = $variableRepository; } /** * Update an existing Egg using an uploaded JSON file. * - * @param int $egg - * @param \Illuminate\Http\UploadedFile $file - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException|\Throwable */ - public function handle(int $egg, UploadedFile $file) + public function handle(Egg $egg, UploadedFile $file): Egg { - if ($file->getError() !== UPLOAD_ERR_OK || ! $file->isFile()) { - throw new InvalidFileUploadException(trans('exceptions.nest.importer.file_error')); + if ($file->getError() !== UPLOAD_ERR_OK || !$file->isFile()) { + throw new InvalidFileUploadException(sprintf('The selected file ["%s"] was not in a valid format to import. (is_file: %s is_valid: %s err_code: %s err: %s)', $file->getFilename(), $file->isFile() ? 'true' : 'false', $file->isValid() ? 'true' : 'false', $file->getError(), $file->getErrorMessage())); } - $parsed = json_decode($file->openFile()->fread($file->getSize())); + $parsed = json_decode($file->openFile()->fread($file->getSize()), true); if (json_last_error() !== 0) { - throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', [ - 'error' => json_last_error_msg(), - ])); + throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', ['error' => json_last_error_msg()])); } + $parsed = $this->eggParserService->handle($parsed); - if (object_get($parsed, 'meta.version') !== 'PTDL_v1') { - throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided')); - } + return $this->connection->transaction(function () use ($egg, $parsed) { + $egg = $this->eggParserService->fillFromParsed($egg, $parsed); + $egg->save(); - $this->connection->beginTransaction(); - $this->repository->update($egg, [ - 'author' => object_get($parsed, 'author'), - 'name' => object_get($parsed, 'name'), - 'description' => object_get($parsed, 'description'), - 'docker_image' => object_get($parsed, 'image'), - 'config_files' => object_get($parsed, 'config.files'), - 'config_startup' => object_get($parsed, 'config.startup'), - 'config_logs' => object_get($parsed, 'config.logs'), - 'config_stop' => object_get($parsed, 'config.stop'), - 'startup' => object_get($parsed, 'startup'), - 'script_install' => object_get($parsed, 'scripts.installation.script'), - 'script_entry' => object_get($parsed, 'scripts.installation.entrypoint'), - 'script_container' => object_get($parsed, 'scripts.installation.container'), - ], true, true); - - // Update Existing Variables - collect($parsed->variables)->each(function ($variable) use ($egg) { - $this->variableRepository->withoutFreshModel()->updateOrCreate([ - 'egg_id' => $egg, - 'env_variable' => $variable->env_variable, - ], collect($variable)->except(['egg_id', 'env_variable'])->toArray()); - }); - - $imported = collect($parsed->variables)->pluck('env_variable')->toArray(); - $existing = $this->variableRepository->setColumns(['id', 'env_variable'])->findWhere([['egg_id', '=', $egg]]); - - // Delete variables not present in the import. - collect($existing)->each(function ($variable) use ($egg, $imported) { - if (! in_array($variable->env_variable, $imported)) { - $this->variableRepository->deleteWhere([ - ['egg_id', '=', $egg], - ['env_variable', '=', $variable->env_variable], - ]); + // Update existing variables or create new ones. + foreach ($parsed['variables'] ?? [] as $variable) { + EggVariable::unguarded(function () use ($egg, $variable) { + $egg->variables()->updateOrCreate([ + 'env_variable' => $variable['env_variable'], + ], Collection::make($variable)->except('egg_id', 'env_variable')->toArray()); + }); } - }); - $this->connection->commit(); + $imported = array_map(fn ($value) => $value['env_variable'], $parsed['variables'] ?? []); + + $egg->variables()->whereNotIn('env_variable', $imported)->delete(); + + return $egg->refresh(); + }); } } diff --git a/app/Services/Eggs/Variables/VariableCreationService.php b/app/Services/Eggs/Variables/VariableCreationService.php index 8a989fba2..a7e19f6ba 100644 --- a/app/Services/Eggs/Variables/VariableCreationService.php +++ b/app/Services/Eggs/Variables/VariableCreationService.php @@ -3,8 +3,8 @@ namespace Pterodactyl\Services\Eggs\Variables; use Pterodactyl\Models\EggVariable; -use Illuminate\Contracts\Validation\Factory; use Pterodactyl\Traits\Services\ValidatesValidationRules; +use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; use Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException; @@ -12,35 +12,18 @@ class VariableCreationService { use ValidatesValidationRules; - /** - * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface - */ - private $repository; - - /** - * @var \Illuminate\Contracts\Validation\Factory - */ - private $validator; - /** * VariableCreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $repository - * @param \Illuminate\Contracts\Validation\Factory $validator */ - public function __construct(EggVariableRepositoryInterface $repository, Factory $validator) + public function __construct(private EggVariableRepositoryInterface $repository, private ValidationFactory $validator) { - $this->repository = $repository; - $this->validator = $validator; } /** * Return the validation factory instance to be used by rule validation * checking in the trait. - * - * @return \Illuminate\Contracts\Validation\Factory */ - protected function getValidator(): Factory + protected function getValidator(): ValidationFactory { return $this->validator; } @@ -48,10 +31,6 @@ class VariableCreationService /** * Create a new variable for a given Egg. * - * @param int $egg - * @param array $data - * @return \Pterodactyl\Models\EggVariable - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException @@ -59,13 +38,10 @@ class VariableCreationService public function handle(int $egg, array $data): EggVariable { if (in_array(strtoupper(array_get($data, 'env_variable')), explode(',', EggVariable::RESERVED_ENV_NAMES))) { - throw new ReservedVariableNameException(sprintf( - 'Cannot use the protected name %s for this environment variable.', - array_get($data, 'env_variable') - )); + throw new ReservedVariableNameException(sprintf('Cannot use the protected name %s for this environment variable.', array_get($data, 'env_variable'))); } - if (! empty($data['rules'] ?? '')) { + if (!empty($data['rules'] ?? '')) { $this->validateRules($data['rules']); } diff --git a/app/Services/Eggs/Variables/VariableUpdateService.php b/app/Services/Eggs/Variables/VariableUpdateService.php index 15bbe2d31..0bd87b98e 100644 --- a/app/Services/Eggs/Variables/VariableUpdateService.php +++ b/app/Services/Eggs/Variables/VariableUpdateService.php @@ -2,46 +2,30 @@ namespace Pterodactyl\Services\Eggs\Variables; +use Illuminate\Support\Str; +use Pterodactyl\Models\Egg; use Pterodactyl\Models\EggVariable; -use Illuminate\Contracts\Validation\Factory; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Traits\Services\ValidatesValidationRules; -use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; +use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException; class VariableUpdateService { use ValidatesValidationRules; - /** - * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface - */ - private $repository; - - /** - * @var \Illuminate\Contracts\Validation\Factory - */ - private $validator; - /** * VariableUpdateService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $repository - * @param \Illuminate\Contracts\Validation\Factory $validator */ - public function __construct(EggVariableRepositoryInterface $repository, Factory $validator) + public function __construct(private ValidationFactory $validator) { - $this->repository = $repository; - $this->validator = $validator; } /** * Return the validation factory instance to be used by rule validation * checking in the trait. - * - * @return \Illuminate\Contracts\Validation\Factory */ - protected function getValidator(): Factory + protected function getValidator(): ValidationFactory { return $this->validator; } @@ -49,50 +33,43 @@ class VariableUpdateService /** * Update a specific egg variable. * - * @param \Pterodactyl\Models\EggVariable $variable - * @param array $data - * @return mixed - * * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException */ - public function handle(EggVariable $variable, array $data) + public function handle(Egg $egg, array $data): void { - if (! is_null(array_get($data, 'env_variable'))) { + if (!is_null(array_get($data, 'env_variable'))) { if (in_array(strtoupper(array_get($data, 'env_variable')), explode(',', EggVariable::RESERVED_ENV_NAMES))) { - throw new ReservedVariableNameException(trans('exceptions.service.variables.reserved_name', [ - 'name' => array_get($data, 'env_variable'), - ])); + throw new ReservedVariableNameException(trans('exceptions.service.variables.reserved_name', ['name' => array_get($data, 'env_variable')])); } - $search = $this->repository->setColumns('id')->findCountWhere([ - ['env_variable', '=', $data['env_variable']], - ['egg_id', '=', $variable->egg_id], - ['id', '!=', $variable->id], - ]); + $count = $egg->variables() + ->where('egg_variables.env_variable', $data['env_variable']) + ->where('egg_variables.id', '!=', $data['id']) + ->count(); - if ($search > 0) { - throw new DisplayException(trans('exceptions.service.variables.env_not_unique', [ - 'name' => array_get($data, 'env_variable'), - ])); + if ($count > 0) { + throw new DisplayException(trans('exceptions.service.variables.env_not_unique', ['name' => array_get($data, 'env_variable')])); } } - if (! empty($data['rules'] ?? '')) { - $this->validateRules($data['rules']); + if (!empty($data['rules'] ?? '')) { + $this->validateRules( + (is_string($data['rules']) && Str::contains($data['rules'], ';;')) + ? explode(';;', $data['rules']) + : $data['rules'] + ); } $options = array_get($data, 'options') ?? []; - return $this->repository->withoutFreshModel()->update($variable->id, [ + $egg->variables()->where('egg_variables.id', $data['id'])->update([ 'name' => $data['name'] ?? '', 'description' => $data['description'] ?? '', 'env_variable' => $data['env_variable'] ?? '', 'default_value' => $data['default_value'] ?? '', - 'user_viewable' => in_array('user_viewable', $options), - 'user_editable' => in_array('user_editable', $options), + 'user_viewable' => $data['user_viewable'], + 'user_editable' => $data['user_editable'], 'rules' => $data['rules'] ?? '', ]); } diff --git a/app/Services/Helpers/ApiAllowedIpsValidatorService.php b/app/Services/Helpers/ApiAllowedIpsValidatorService.php deleted file mode 100644 index 7051cd539..000000000 --- a/app/Services/Helpers/ApiAllowedIpsValidatorService.php +++ /dev/null @@ -1,8 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ diff --git a/app/Services/Helpers/SoftwareVersionService.php b/app/Services/Helpers/SoftwareVersionService.php index 0d91bb9aa..ad5ca3789 100644 --- a/app/Services/Helpers/SoftwareVersionService.php +++ b/app/Services/Helpers/SoftwareVersionService.php @@ -1,134 +1,167 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Helpers; -use stdClass; use Exception; -use GuzzleHttp\Client; +use Carbon\CarbonImmutable; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; +use Illuminate\Support\Facades\Http; use Illuminate\Contracts\Cache\Repository as CacheRepository; -use Illuminate\Contracts\Config\Repository as ConfigRepository; use Pterodactyl\Exceptions\Service\Helper\CdnVersionFetchingException; class SoftwareVersionService { - const VERSION_CACHE_KEY = 'pterodactyl:versions'; + public const VERSION_CACHE_KEY = 'pterodactyl:versioning_data'; + public const GIT_VERSION_CACHE_KEY = 'pterodactyl:git_data'; - /** - * @var \Illuminate\Contracts\Cache\Repository - */ - protected $cache; - - /** - * @var \GuzzleHttp\Client - */ - protected $client; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; + private static array $result; /** * SoftwareVersionService constructor. - * - * @param \Illuminate\Contracts\Cache\Repository $cache - * @param \GuzzleHttp\Client $client - * @param \Illuminate\Contracts\Config\Repository $config */ - public function __construct( - CacheRepository $cache, - Client $client, - ConfigRepository $config - ) { - $this->cache = $cache; - $this->client = $client; - $this->config = $config; - - $this->cacheVersionData(); + public function __construct(private CacheRepository $cache) + { + self::$result = $this->cacheVersionData(); } /** - * Get the latest version of the panel from the CDN servers. - * - * @return string + * Return the current version of the panel that is being used. */ - public function getPanel() + public function getCurrentVersion(): string { - return object_get($this->cache->get(self::VERSION_CACHE_KEY), 'panel', 'error'); + return config('app.version'); } /** - * Get the latest version of the daemon from the CDN servers. - * - * @return string + * Returns the latest version of the panel from the CDN servers. */ - public function getDaemon() + public function getLatestPanel(): string { - return object_get($this->cache->get(self::VERSION_CACHE_KEY), 'daemon', 'error'); + return Arr::get(self::$result, 'panel') ?? 'error'; } /** - * Get the URL to the discord server. - * - * @return string + * Returns the latest version of the Wings from the CDN servers. */ - public function getDiscord() + public function getLatestWings(): string { - return object_get($this->cache->get(self::VERSION_CACHE_KEY), 'discord', 'https://pterodactyl.io/discord'); + return Arr::get(self::$result, 'wings') ?? 'error'; + } + + /** + * Returns the URL to the discord server. + */ + public function getDiscord(): string + { + return Arr::get(self::$result, 'discord') ?? 'https://pterodactyl.io/discord'; + } + + /** + * Returns the URL for donations. + */ + public function getDonations(): string + { + return Arr::get(self::$result, 'donations') ?? 'https://github.com/sponsors/matthewpi'; } /** * Determine if the current version of the panel is the latest. - * - * @return bool */ - public function isLatestPanel() + public function isLatestPanel(): bool { - if ($this->config->get('app.version') === 'canary') { + $version = $this->getCurrentVersion(); + if ($version === 'canary') { return true; } - return version_compare($this->config->get('app.version'), $this->getPanel()) >= 0; + return version_compare($version, $this->getLatestPanel()) >= 0; } /** * Determine if a passed daemon version string is the latest. - * - * @param string $version - * @return bool */ - public function isLatestDaemon($version) + public function isLatestWings(string $version): bool { - if ($version === '0.0.0-canary') { + if ($version === 'develop' || Str::startsWith($version, 'dev-')) { return true; } - return version_compare($version, $this->getDaemon()) >= 0; + return version_compare($version, $this->getLatestWings()) >= 0; + } + + /** + * ? + */ + public function getVersionData(): array + { + $versionData = $this->versionData(); + if ($versionData['is_git']) { + $git = $versionData['version']; + } else { + $git = null; + } + + return [ + 'panel' => [ + 'current' => $this->getCurrentVersion(), + 'latest' => $this->getLatestPanel(), + ], + + 'wings' => [ + 'latest' => $this->getLatestWings(), + ], + + 'git' => $git, + ]; + } + + /** + * Return version information for the footer. + */ + protected function versionData(): array + { + return $this->cache->remember(self::GIT_VERSION_CACHE_KEY, CarbonImmutable::now()->addSeconds(15), function () { + $configVersion = $this->getCurrentVersion(); + + if (file_exists(base_path('.git/HEAD'))) { + $head = explode(' ', file_get_contents(base_path('.git/HEAD'))); + + if (array_key_exists(1, $head)) { + $path = base_path('.git/' . trim($head[1])); + } + } + + if (isset($path) && file_exists($path)) { + return [ + 'version' => substr(file_get_contents($path), 0, 8), + 'is_git' => true, + ]; + } + + return [ + 'version' => $configVersion, + 'is_git' => false, + ]; + }); } /** * Keeps the versioning cache up-to-date with the latest results from the CDN. */ - protected function cacheVersionData() + protected function cacheVersionData(): array { - $this->cache->remember(self::VERSION_CACHE_KEY, $this->config->get('pterodactyl.cdn.cache_time'), function () { + return $this->cache->remember(self::VERSION_CACHE_KEY, CarbonImmutable::now()->addMinutes(config('pterodactyl.cdn.cache_time', 60)), function () { try { - $response = $this->client->request('GET', $this->config->get('pterodactyl.cdn.url')); + $response = Http::get(config('pterodactyl.cdn.url')); - if ($response->getStatusCode() === 200) { - return json_decode($response->getBody()); + if ($response->status() === 200) { + return json_decode($response->body(), true); } - throw new CdnVersionFetchingException; - } catch (Exception $exception) { - return new stdClass(); + throw new CdnVersionFetchingException(); + } catch (Exception) { + return []; } }); } diff --git a/app/Services/Locations/LocationCreationService.php b/app/Services/Locations/LocationCreationService.php index cd2a97572..b1a3ec995 100644 --- a/app/Services/Locations/LocationCreationService.php +++ b/app/Services/Locations/LocationCreationService.php @@ -1,42 +1,25 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Locations; +use Pterodactyl\Models\Location; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; class LocationCreationService { - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $repository; - /** * LocationCreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository */ - public function __construct(LocationRepositoryInterface $repository) + public function __construct(protected LocationRepositoryInterface $repository) { - $this->repository = $repository; } /** * Create a new location. * - * @param array $data - * @return \Pterodactyl\Models\Location - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function handle(array $data) + public function handle(array $data): Location { return $this->repository->create($data); } diff --git a/app/Services/Locations/LocationDeletionService.php b/app/Services/Locations/LocationDeletionService.php index 5d1495d16..5b4b9eba4 100644 --- a/app/Services/Locations/LocationDeletionService.php +++ b/app/Services/Locations/LocationDeletionService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Locations; @@ -17,39 +10,21 @@ use Pterodactyl\Exceptions\Service\Location\HasActiveNodesException; class LocationDeletionService { - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - protected $nodeRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $repository; - /** * LocationDeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $nodeRepository */ public function __construct( - LocationRepositoryInterface $repository, - NodeRepositoryInterface $nodeRepository + protected LocationRepositoryInterface $repository, + protected NodeRepositoryInterface $nodeRepository ) { - $this->nodeRepository = $nodeRepository; - $this->repository = $repository; } /** * Delete an existing location. * - * @param int|\Pterodactyl\Models\Location $location - * @return int|null - * * @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException */ - public function handle($location) + public function handle(Location|int $location): ?int { $location = ($location instanceof Location) ? $location->id : $location; diff --git a/app/Services/Locations/LocationUpdateService.php b/app/Services/Locations/LocationUpdateService.php index a245e71b1..cf24459e9 100644 --- a/app/Services/Locations/LocationUpdateService.php +++ b/app/Services/Locations/LocationUpdateService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Locations; @@ -14,32 +7,20 @@ use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; class LocationUpdateService { - /** - * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface - */ - protected $repository; - /** * LocationUpdateService constructor. - * - * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository */ - public function __construct(LocationRepositoryInterface $repository) + public function __construct(protected LocationRepositoryInterface $repository) { - $this->repository = $repository; } /** * Update an existing location. * - * @param int|\Pterodactyl\Models\Location $location - * @param array $data - * @return \Pterodactyl\Models\Location - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function handle($location, array $data) + public function handle(Location|int $location, array $data): Location { $location = ($location instanceof Location) ? $location->id : $location; diff --git a/app/Services/Nests/NestCreationService.php b/app/Services/Nests/NestCreationService.php index 439164485..c3513aefe 100644 --- a/app/Services/Nests/NestCreationService.php +++ b/app/Services/Nests/NestCreationService.php @@ -9,34 +9,16 @@ use Illuminate\Contracts\Config\Repository as ConfigRepository; class NestCreationService { - /** - * @var \Illuminate\Contracts\Config\Repository - */ - private $config; - - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - private $repository; - /** * NestCreationService constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $repository */ - public function __construct(ConfigRepository $config, NestRepositoryInterface $repository) + public function __construct(private ConfigRepository $config, private NestRepositoryInterface $repository) { - $this->config = $config; - $this->repository = $repository; } /** * Create a new nest on the system. * - * @param array $data - * @param string|null $author - * @return \Pterodactyl\Models\Nest * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ public function handle(array $data, string $author = null): Nest diff --git a/app/Services/Nests/NestDeletionService.php b/app/Services/Nests/NestDeletionService.php index 0f01a5e58..6babf45c3 100644 --- a/app/Services/Nests/NestDeletionService.php +++ b/app/Services/Nests/NestDeletionService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Nests; @@ -15,36 +8,18 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class NestDeletionService { - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - protected $repository; - /** * NestDeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $repository */ public function __construct( - ServerRepositoryInterface $serverRepository, - NestRepositoryInterface $repository + protected ServerRepositoryInterface $serverRepository, + protected NestRepositoryInterface $repository ) { - $this->serverRepository = $serverRepository; - $this->repository = $repository; } /** * Delete a nest from the system only if there are no servers attached to it. * - * @param int $nest - * @return int - * * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException */ public function handle(int $nest): int diff --git a/app/Services/Nests/NestUpdateService.php b/app/Services/Nests/NestUpdateService.php index c3cdcdd5d..772421e42 100644 --- a/app/Services/Nests/NestUpdateService.php +++ b/app/Services/Nests/NestUpdateService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Nests; @@ -13,32 +6,22 @@ use Pterodactyl\Contracts\Repository\NestRepositoryInterface; class NestUpdateService { - /** - * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface - */ - protected $repository; - /** * NestUpdateService constructor. - * - * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $repository */ - public function __construct(NestRepositoryInterface $repository) + public function __construct(protected NestRepositoryInterface $repository) { - $this->repository = $repository; } /** * Update a nest and prevent changing the author once it is set. * - * @param int $nest - * @param array $data * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function handle(int $nest, array $data) + public function handle(int $nest, array $data): void { - if (! is_null(array_get($data, 'author'))) { + if (!is_null(array_get($data, 'author'))) { unset($data['author']); } diff --git a/app/Services/Nodes/NodeCreationService.php b/app/Services/Nodes/NodeCreationService.php index 889e81a20..b21589c4a 100644 --- a/app/Services/Nodes/NodeCreationService.php +++ b/app/Services/Nodes/NodeCreationService.php @@ -1,47 +1,33 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Nodes; +use Ramsey\Uuid\Uuid; +use Illuminate\Support\Str; +use Pterodactyl\Models\Node; +use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; class NodeCreationService { - const DAEMON_SECRET_LENGTH = 36; - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface + * NodeCreationService constructor. */ - protected $repository; - - /** - * CreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository - */ - public function __construct(NodeRepositoryInterface $repository) + public function __construct(protected NodeRepositoryInterface $repository) { - $this->repository = $repository; } /** * Create a new node on the panel. * - * @param array $data - * @return \Pterodactyl\Models\Node - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function handle(array $data) + public function handle(array $data): Node { - $data['daemonSecret'] = str_random(self::DAEMON_SECRET_LENGTH); + $data['uuid'] = Uuid::uuid4()->toString(); + $data['daemon_token'] = app(Encrypter::class)->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); - return $this->repository->create($data); + return $this->repository->create($data, true, true); } } diff --git a/app/Services/Nodes/NodeDeletionService.php b/app/Services/Nodes/NodeDeletionService.php index e4d2ed999..adb9a0628 100644 --- a/app/Services/Nodes/NodeDeletionService.php +++ b/app/Services/Nodes/NodeDeletionService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Nodes; @@ -18,46 +11,21 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class NodeDeletionService { /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * @var \Illuminate\Contracts\Translation\Translator - */ - protected $translator; - - /** - * DeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Illuminate\Contracts\Translation\Translator $translator + * NodeDeletionService constructor. */ public function __construct( - NodeRepositoryInterface $repository, - ServerRepositoryInterface $serverRepository, - Translator $translator + protected NodeRepositoryInterface $repository, + protected ServerRepositoryInterface $serverRepository, + protected Translator $translator ) { - $this->repository = $repository; - $this->serverRepository = $serverRepository; - $this->translator = $translator; } /** * Delete a node from the panel if no servers are attached to it. * - * @param int|\Pterodactyl\Models\Node $node - * @return bool|null - * * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException */ - public function handle($node) + public function handle(int|Node $node): int { if ($node instanceof Node) { $node = $node->id; @@ -65,7 +33,7 @@ class NodeDeletionService $servers = $this->serverRepository->setColumns('id')->findCountWhere([['node_id', '=', $node]]); if ($servers > 0) { - throw new HasActiveServersException($this->translator->trans('exceptions.node.servers_attached')); + throw new HasActiveServersException($this->translator->get('exceptions.node.servers_attached')); } return $this->repository->delete($node); diff --git a/app/Services/Nodes/NodeJWTService.php b/app/Services/Nodes/NodeJWTService.php new file mode 100644 index 000000000..105715a40 --- /dev/null +++ b/app/Services/Nodes/NodeJWTService.php @@ -0,0 +1,103 @@ +claims = $claims; + + return $this; + } + + /** + * Attaches a user to the JWT being created and will automatically inject the + * "user_uuid" key into the final claims array with the user's UUID. + */ + public function setUser(User $user): self + { + $this->user = $user; + + return $this; + } + + public function setExpiresAt(\DateTimeImmutable $date): self + { + $this->expiresAt = $date; + + return $this; + } + + public function setSubject(string $subject): self + { + $this->subject = $subject; + + return $this; + } + + /** + * Generate a new JWT for a given node. + */ + public function handle(Node $node, ?string $identifiedBy, string $algo = 'md5'): Plain + { + $identifier = hash($algo, $identifiedBy); + $config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText($node->getDecryptedKey())); + + $builder = $config->builder(new TimestampDates()) + ->issuedBy(config('app.url')) + ->permittedFor($node->getConnectionAddress()) + ->identifiedBy($identifier) + ->withHeader('jti', $identifier) + ->issuedAt(CarbonImmutable::now()) + ->canOnlyBeUsedAfter(CarbonImmutable::now()->subMinutes(5)); + + if ($this->expiresAt) { + $builder = $builder->expiresAt($this->expiresAt); + } + + if (!empty($this->subject)) { + $builder = $builder->relatedTo($this->subject)->withHeader('sub', $this->subject); + } + + foreach ($this->claims as $key => $value) { + $builder = $builder->withClaim($key, $value); + } + + if (!is_null($this->user)) { + $builder = $builder + ->withClaim('user_uuid', $this->user->uuid) + // The "user_id" claim is deprecated and should not be referenced — it remains + // here solely to ensure older versions of Wings are unaffected when the Panel + // is updated. + // + // This claim will be removed in Panel@1.11 or later. + ->withClaim('user_id', $this->user->id); + } + + return $builder + ->withClaim('unique_id', Str::random()) + ->getToken($config->signer(), $config->signingKey()); + } +} diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php index 219e2a34f..28733e35a 100644 --- a/app/Services/Nodes/NodeUpdateService.php +++ b/app/Services/Nodes/NodeUpdateService.php @@ -2,102 +2,79 @@ namespace Pterodactyl\Services\Nodes; +use Illuminate\Support\Str; use Pterodactyl\Models\Node; -use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Exception\RequestException; +use Illuminate\Support\Facades\Log; use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; +use Illuminate\Contracts\Encryption\Encrypter; +use Pterodactyl\Repositories\Eloquent\NodeRepository; +use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException; -use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface; class NodeUpdateService { /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface - */ - private $configRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - private $repository; - - /** - * UpdateService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface $configurationRepository - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository + * NodeUpdateService constructor. */ public function __construct( - ConnectionInterface $connection, - ConfigurationRepositoryInterface $configurationRepository, - NodeRepositoryInterface $repository + private ConnectionInterface $connection, + private DaemonConfigurationRepository $configurationRepository, + private Encrypter $encrypter, + private NodeRepository $repository ) { - $this->connection = $connection; - $this->configRepository = $configurationRepository; - $this->repository = $repository; } /** * Update the configuration values for a given node on the machine. * - * @param \Pterodactyl\Models\Node $node - * @param array $data - * @param bool $resetToken - * - * @return \Pterodactyl\Models\Node - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException + * @throws \Throwable */ - public function handle(Node $node, array $data, bool $resetToken = false) + public function handle(Node $node, array $data, bool $resetToken = false): Node { if ($resetToken) { - $data['daemonSecret'] = str_random(Node::DAEMON_SECRET_LENGTH); + $data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); } - $this->connection->beginTransaction(); + [$updated, $exception] = $this->connection->transaction(function () use ($data, $node) { + /** @var \Pterodactyl\Models\Node $updated */ + $updated = $this->repository->withFreshModel()->update($node->id, $data, true, true); - /** @var \Pterodactyl\Models\Node $updatedModel */ - $updatedModel = $this->repository->update($node->id, $data); + try { + // If we're changing the FQDN for the node, use the newly provided FQDN for the connection + // address. This should alleviate issues where the node gets pointed to a "valid" FQDN that + // isn't actually running the daemon software, and therefore you can't actually change it + // back. + // + // This makes more sense anyways, because only the Panel uses the FQDN for connecting, the + // node doesn't actually care about this. + // + // @see https://github.com/pterodactyl/panel/issues/1931 + $node->fqdn = $updated->fqdn; - try { - if ($resetToken) { - // We need to clone the new model and set it's authentication token to be the - // old one so we can connect. Then we will pass the new token through as an - // override on the call. - $cloned = $updatedModel->replicate(['daemonSecret']); - $cloned->setAttribute('daemonSecret', $node->getAttribute('daemonSecret')); + $this->configurationRepository->setNode($node)->update($updated); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['node_id' => $node->id]); - $this->configRepository->setNode($cloned)->update([ - 'keys' => [$data['daemonSecret']], - ]); - } else { - $this->configRepository->setNode($updatedModel)->update(); + // Never actually throw these exceptions up the stack. If we were able to change the settings + // but something went wrong with Wings we just want to store the update and let the user manually + // make changes as needed. + // + // This avoids issues with proxies such as Cloudflare which will see Wings as offline and then + // inject their own response pages, causing this logic to get fucked up. + // + // @see https://github.com/pterodactyl/panel/issues/2712 + return [$updated, true]; } - $this->connection->commit(); - } catch (RequestException $exception) { - // Failed to connect to the Daemon. Let's go ahead and save the configuration - // and let the user know they'll need to manually update. - if ($exception instanceof ConnectException) { - $this->connection->commit(); + return [$updated, false]; + }); - throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); - } - - throw new DaemonConnectionException($exception); + if ($exception) { + throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); } - return $updatedModel; + return $updated; } } diff --git a/app/Services/Packs/ExportPackService.php b/app/Services/Packs/ExportPackService.php deleted file mode 100644 index 24b4db96b..000000000 --- a/app/Services/Packs/ExportPackService.php +++ /dev/null @@ -1,97 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Packs; - -use ZipArchive; -use Pterodactyl\Models\Pack; -use Pterodactyl\Contracts\Repository\PackRepositoryInterface; -use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; -use Pterodactyl\Exceptions\Service\Pack\ZipArchiveCreationException; - -class ExportPackService -{ - /** - * @var \ZipArchive - */ - protected $archive; - - /** - * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface - */ - protected $repository; - - /** - * @var \Illuminate\Contracts\Filesystem\Factory - */ - protected $storage; - - /** - * ExportPackService constructor. - * - * @param \Illuminate\Contracts\Filesystem\Factory $storage - * @param \Pterodactyl\Contracts\Repository\PackRepositoryInterface $repository - * @param \ZipArchive $archive - */ - public function __construct( - FilesystemFactory $storage, - PackRepositoryInterface $repository, - ZipArchive $archive - ) { - $this->archive = $archive; - $this->repository = $repository; - $this->storage = $storage; - } - - /** - * Prepare a pack for export. - * - * @param int|\Pterodactyl\Models\Pack $pack - * @param bool $files - * @return string - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Pack\ZipArchiveCreationException - */ - public function handle($pack, $files = false) - { - if (! $pack instanceof Pack) { - $pack = $this->repository->find($pack); - } - - $json = [ - 'name' => $pack->name, - 'version' => $pack->version, - 'description' => $pack->description, - 'selectable' => $pack->selectable, - 'visible' => $pack->visible, - 'locked' => $pack->locked, - ]; - - $filename = tempnam(sys_get_temp_dir(), 'pterodactyl_'); - if ($files) { - if (! $this->archive->open($filename, $this->archive::CREATE)) { - throw new ZipArchiveCreationException; - } - - foreach ($this->storage->disk()->files('packs/' . $pack->uuid) as $file) { - $this->archive->addFile(storage_path('app/' . $file), basename(storage_path('app/' . $file))); - } - - $this->archive->addFromString('import.json', json_encode($json, JSON_PRETTY_PRINT)); - $this->archive->close(); - } else { - $fp = fopen($filename, 'a+'); - fwrite($fp, json_encode($json, JSON_PRETTY_PRINT)); - fclose($fp); - } - - return $filename; - } -} diff --git a/app/Services/Packs/PackCreationService.php b/app/Services/Packs/PackCreationService.php deleted file mode 100644 index 80b452d88..000000000 --- a/app/Services/Packs/PackCreationService.php +++ /dev/null @@ -1,104 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Packs; - -use Ramsey\Uuid\Uuid; -use Illuminate\Http\UploadedFile; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\PackRepositoryInterface; -use Pterodactyl\Exceptions\Service\InvalidFileUploadException; -use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; -use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException; - -class PackCreationService -{ - const VALID_UPLOAD_TYPES = [ - 'application/gzip', - 'application/x-gzip', - ]; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface - */ - protected $repository; - - /** - * @var \Illuminate\Contracts\Filesystem\Factory - */ - protected $storage; - - /** - * PackCreationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Illuminate\Contracts\Filesystem\Factory $storage - * @param \Pterodactyl\Contracts\Repository\PackRepositoryInterface $repository - */ - public function __construct( - ConnectionInterface $connection, - FilesystemFactory $storage, - PackRepositoryInterface $repository - ) { - $this->connection = $connection; - $this->repository = $repository; - $this->storage = $storage; - } - - /** - * Add a new service pack to the system. - * - * @param array $data - * @param \Illuminate\Http\UploadedFile|null $file - * @return \Pterodactyl\Models\Pack - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException - */ - public function handle(array $data, UploadedFile $file = null) - { - if (! is_null($file)) { - if (! $file->isValid()) { - throw new InvalidFileUploadException(trans('exceptions.packs.invalid_upload')); - } - - if (! in_array($file->getMimeType(), self::VALID_UPLOAD_TYPES)) { - throw new InvalidFileMimeTypeException(trans('exceptions.packs.invalid_mime', [ - 'type' => implode(', ', self::VALID_UPLOAD_TYPES), - ])); - } - } - - // Transform values to boolean - $data['selectable'] = isset($data['selectable']); - $data['visible'] = isset($data['visible']); - $data['locked'] = isset($data['locked']); - - $this->connection->beginTransaction(); - $pack = $this->repository->create(array_merge( - ['uuid' => Uuid::uuid4()], - $data - )); - - $this->storage->disk()->makeDirectory('packs/' . $pack->uuid); - if (! is_null($file)) { - $file->storeAs('packs/' . $pack->uuid, 'archive.tar.gz'); - } - - $this->connection->commit(); - - return $pack; - } -} diff --git a/app/Services/Packs/PackDeletionService.php b/app/Services/Packs/PackDeletionService.php deleted file mode 100644 index 9d4743310..000000000 --- a/app/Services/Packs/PackDeletionService.php +++ /dev/null @@ -1,85 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Packs; - -use Pterodactyl\Models\Pack; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\PackRepositoryInterface; -use Pterodactyl\Exceptions\Service\HasActiveServersException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; - -class PackDeletionService -{ - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * @var \Illuminate\Contracts\Filesystem\Factory - */ - protected $storage; - - /** - * PackDeletionService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Illuminate\Contracts\Filesystem\Factory $storage - * @param \Pterodactyl\Contracts\Repository\PackRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - */ - public function __construct( - ConnectionInterface $connection, - FilesystemFactory $storage, - PackRepositoryInterface $repository, - ServerRepositoryInterface $serverRepository - ) { - $this->connection = $connection; - $this->repository = $repository; - $this->serverRepository = $serverRepository; - $this->storage = $storage; - } - - /** - * Delete a pack from the database as well as the archive stored on the server. - * - * @param int|\Pterodactyl\Models\Pack$pack - * - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle($pack) - { - if (! $pack instanceof Pack) { - $pack = $this->repository->setColumns(['id', 'uuid'])->find($pack); - } - - $count = $this->serverRepository->findCountWhere([['pack_id', '=', $pack->id]]); - if ($count !== 0) { - throw new HasActiveServersException(trans('exceptions.packs.delete_has_servers')); - } - - $this->connection->beginTransaction(); - $this->repository->delete($pack->id); - $this->storage->disk()->deleteDirectory('packs/' . $pack->uuid); - $this->connection->commit(); - } -} diff --git a/app/Services/Packs/PackUpdateService.php b/app/Services/Packs/PackUpdateService.php deleted file mode 100644 index a22a55b9f..000000000 --- a/app/Services/Packs/PackUpdateService.php +++ /dev/null @@ -1,75 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Packs; - -use Pterodactyl\Models\Pack; -use Pterodactyl\Contracts\Repository\PackRepositoryInterface; -use Pterodactyl\Exceptions\Service\HasActiveServersException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; - -class PackUpdateService -{ - /** - * @var \Pterodactyl\Contracts\Repository\PackRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * PackUpdateService constructor. - * - * @param \Pterodactyl\Contracts\Repository\PackRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - */ - public function __construct( - PackRepositoryInterface $repository, - ServerRepositoryInterface $serverRepository - ) { - $this->repository = $repository; - $this->serverRepository = $serverRepository; - } - - /** - * Update a pack. - * - * @param int|\Pterodactyl\Models\Pack $pack - * @param array $data - * @return bool - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle($pack, array $data) - { - if (! $pack instanceof Pack) { - $pack = $this->repository->setColumns(['id', 'egg_id'])->find($pack); - } - - if ((int) array_get($data, 'egg_id', $pack->egg_id) !== $pack->egg_id) { - $count = $this->serverRepository->findCountWhere([['pack_id', '=', $pack->id]]); - - if ($count !== 0) { - throw new HasActiveServersException(trans('exceptions.packs.update_has_servers')); - } - } - - // Transform values to boolean - $data['selectable'] = isset($data['selectable']); - $data['visible'] = isset($data['visible']); - $data['locked'] = isset($data['locked']); - - return $this->repository->withoutFreshModel()->update($pack->id, $data); - } -} diff --git a/app/Services/Packs/TemplateUploadService.php b/app/Services/Packs/TemplateUploadService.php deleted file mode 100644 index 6495edd68..000000000 --- a/app/Services/Packs/TemplateUploadService.php +++ /dev/null @@ -1,125 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Packs; - -use ZipArchive; -use Illuminate\Http\UploadedFile; -use Pterodactyl\Exceptions\Service\InvalidFileUploadException; -use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException; -use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException; -use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException; -use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException; - -class TemplateUploadService -{ - const VALID_UPLOAD_TYPES = [ - 'application/zip', - 'text/plain', - 'application/json', - ]; - - /** - * @var \ZipArchive - */ - protected $archive; - - /** - * @var \Pterodactyl\Services\Packs\PackCreationService - */ - protected $creationService; - - /** - * TemplateUploadService constructor. - * - * @param \Pterodactyl\Services\Packs\PackCreationService $creationService - * @param \ZipArchive $archive - */ - public function __construct( - PackCreationService $creationService, - ZipArchive $archive - ) { - $this->archive = $archive; - $this->creationService = $creationService; - } - - /** - * Process an uploaded file to create a new pack from a JSON or ZIP format. - * - * @param int $egg - * @param \Illuminate\Http\UploadedFile $file - * @return \Pterodactyl\Models\Pack - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException - * @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException - */ - public function handle($egg, UploadedFile $file) - { - if (! $file->isValid()) { - throw new InvalidFileUploadException(trans('exceptions.packs.invalid_upload')); - } - - if (! in_array($file->getMimeType(), self::VALID_UPLOAD_TYPES)) { - throw new InvalidFileMimeTypeException(trans('exceptions.packs.invalid_mime', [ - 'type' => implode(', ', self::VALID_UPLOAD_TYPES), - ])); - } - - if ($file->getMimeType() === 'application/zip') { - return $this->handleArchive($egg, $file); - } else { - $json = json_decode($file->openFile()->fread($file->getSize()), true); - $json['egg_id'] = $egg; - - return $this->creationService->handle($json); - } - } - - /** - * Process a ZIP file to create a pack and stored archive. - * - * @param int $egg - * @param \Illuminate\Http\UploadedFile $file - * @return \Pterodactyl\Models\Pack - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException - * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException - * @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException - */ - protected function handleArchive($egg, $file) - { - if (! $this->archive->open($file->getRealPath())) { - throw new UnreadableZipArchiveException(trans('exceptions.packs.unreadable')); - } - - if (! $this->archive->locateName('import.json') || ! $this->archive->locateName('archive.tar.gz')) { - throw new InvalidPackArchiveFormatException(trans('exceptions.packs.invalid_archive_exception')); - } - - $json = json_decode($this->archive->getFromName('import.json'), true); - $json['egg_id'] = $egg; - - $pack = $this->creationService->handle($json); - if (! $this->archive->extractTo(storage_path('app/packs/' . $pack->uuid), 'archive.tar.gz')) { - // @todo delete the pack that was created. - throw new ZipExtractionException(trans('exceptions.packs.zip_extraction')); - } - - $this->archive->close(); - - return $pack; - } -} diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php index 4ada2aee8..322814acc 100644 --- a/app/Services/Schedules/ProcessScheduleService.php +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -2,78 +2,87 @@ namespace Pterodactyl\Services\Schedules; -use Cron\CronExpression; +use Exception; use Pterodactyl\Models\Schedule; use Illuminate\Contracts\Bus\Dispatcher; use Pterodactyl\Jobs\Schedule\RunTaskJob; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; -use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; +use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class ProcessScheduleService { - /** - * @var \Illuminate\Contracts\Bus\Dispatcher - */ - private $dispatcher; - - /** - * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface - */ - private $scheduleRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface - */ - private $taskRepository; - /** * ProcessScheduleService constructor. - * - * @param \Illuminate\Contracts\Bus\Dispatcher $dispatcher - * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $scheduleRepository - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $taskRepository */ - public function __construct( - Dispatcher $dispatcher, - ScheduleRepositoryInterface $scheduleRepository, - TaskRepositoryInterface $taskRepository - ) { - $this->dispatcher = $dispatcher; - $this->scheduleRepository = $scheduleRepository; - $this->taskRepository = $taskRepository; + public function __construct(private ConnectionInterface $connection, private Dispatcher $dispatcher, private DaemonServerRepository $serverRepository) + { } /** * Process a schedule and push the first task onto the queue worker. * - * @param \Pterodactyl\Models\Schedule $schedule - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function handle(Schedule $schedule) + public function handle(Schedule $schedule, bool $now = false): void { - $this->scheduleRepository->loadTasks($schedule); + $task = $schedule->tasks()->orderBy('sequence_id')->first(); - /** @var \Pterodactyl\Models\Task $task */ - $task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first(); + if (is_null($task)) { + throw new DisplayException('Cannot process schedule for task execution: no tasks are registered.'); + } - $formattedCron = sprintf('%s %s %s * %s', - $schedule->cron_minute, - $schedule->cron_hour, - $schedule->cron_day_of_month, - $schedule->cron_day_of_week - ); + /* @var \Pterodactyl\Models\Task $task */ + $this->connection->transaction(function () use ($schedule, $task) { + $schedule->forceFill([ + 'is_processing' => true, + 'next_run_at' => $schedule->getNextRunDate(), + ])->saveOrFail(); - $this->scheduleRepository->update($schedule->id, [ - 'is_processing' => true, - 'next_run_at' => CronExpression::factory($formattedCron)->getNextRunDate(), - ]); + $task->update(['is_queued' => true]); + }); - $this->taskRepository->update($task->id, ['is_queued' => true]); + $job = new RunTaskJob($task, $now); + if ($schedule->only_when_online) { + // Check that the server is currently in a starting or running state before executing + // this schedule if this option has been set. + try { + $details = $this->serverRepository->setServer($schedule->server)->getDetails(); + $state = $details['state'] ?? 'offline'; + // If the server is stopping or offline just do nothing with this task. + if (in_array($state, ['offline', 'stopping'])) { + $job->failed(); - $this->dispatcher->dispatch( - (new RunTaskJob($task->id, $schedule->id))->delay($task->time_offset) - ); + return; + } + } catch (\Exception $exception) { + if (!$exception instanceof DaemonConnectionException) { + // If we encountered some exception during this process that wasn't just an + // issue connecting to Wings run the failed sequence for a job. Otherwise we + // can just quietly mark the task as completed without actually running anything. + $job->failed($exception); + } + $job->failed(); + + return; + } + } + + if (!$now) { + $this->dispatcher->dispatch($job->delay($task->time_offset)); + } else { + // When using dispatchNow the RunTaskJob::failed() function is not called automatically + // so we need to manually trigger it and then continue with the exception throw. + // + // @see https://github.com/pterodactyl/panel/issues/2550 + try { + $this->dispatcher->dispatchNow($job); + } catch (\Exception $exception) { + $job->failed($exception); + + throw $exception; + } + } } } diff --git a/app/Services/Schedules/ScheduleCreationService.php b/app/Services/Schedules/ScheduleCreationService.php deleted file mode 100644 index c8cbd7fb3..000000000 --- a/app/Services/Schedules/ScheduleCreationService.php +++ /dev/null @@ -1,98 +0,0 @@ -connection = $connection; - $this->repository = $repository; - $this->taskCreationService = $taskCreationService; - } - - /** - * Create a new schedule for a specific server. - * - * @param \Pterodactyl\Models\Server $server - * @param array $data - * @param array $tasks - * @return \Pterodactyl\Models\Schedule - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException - */ - public function handle(Server $server, array $data, array $tasks = []) - { - $data = array_merge($data, [ - 'server_id' => $server->id, - 'next_run_at' => $this->getCronTimestamp($data), - ]); - - $this->connection->beginTransaction(); - $schedule = $this->repository->create($data); - - foreach ($tasks as $index => $task) { - $this->taskCreationService->handle($schedule, [ - 'time_interval' => array_get($task, 'time_interval'), - 'time_value' => array_get($task, 'time_value'), - 'sequence_id' => $index + 1, - 'action' => array_get($task, 'action'), - 'payload' => array_get($task, 'payload'), - ], false); - } - - $this->connection->commit(); - - return $schedule; - } - - /** - * Return a DateTime object after parsing the cron data provided. - * - * @param array $data - * @return \DateTime - */ - private function getCronTimestamp(array $data) - { - $formattedCron = sprintf('%s %s %s * %s', - array_get($data, 'cron_minute', '*'), - array_get($data, 'cron_hour', '*'), - array_get($data, 'cron_day_of_month', '*'), - array_get($data, 'cron_day_of_week', '*') - ); - - return CronExpression::factory($formattedCron)->getNextRunDate(); - } -} diff --git a/app/Services/Schedules/ScheduleUpdateService.php b/app/Services/Schedules/ScheduleUpdateService.php deleted file mode 100644 index 1ddbbd242..000000000 --- a/app/Services/Schedules/ScheduleUpdateService.php +++ /dev/null @@ -1,110 +0,0 @@ -connection = $connection; - $this->repository = $repository; - $this->taskCreationService = $taskCreationService; - $this->taskRepository = $taskRepository; - } - - /** - * Update an existing schedule by deleting all current tasks and re-inserting the - * new values. - * - * @param \Pterodactyl\Models\Schedule $schedule - * @param array $data - * @param array $tasks - * @return \Pterodactyl\Models\Schedule - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException - */ - public function handle(Schedule $schedule, array $data, array $tasks): Schedule - { - $data = array_merge($data, [ - 'next_run_at' => $this->getCronTimestamp($data), - ]); - - $this->connection->beginTransaction(); - - $schedule = $this->repository->update($schedule->id, $data); - $this->taskRepository->deleteWhere([['schedule_id', '=', $schedule->id]]); - - foreach ($tasks as $index => $task) { - $this->taskCreationService->handle($schedule, [ - 'time_interval' => array_get($task, 'time_interval'), - 'time_value' => array_get($task, 'time_value'), - 'sequence_id' => $index + 1, - 'action' => array_get($task, 'action'), - 'payload' => array_get($task, 'payload'), - ], false); - } - - $this->connection->commit(); - - return $schedule; - } - - /** - * Return a DateTime object after parsing the cron data provided. - * - * @param array $data - * @return \DateTime - */ - private function getCronTimestamp(array $data) - { - $formattedCron = sprintf('%s %s %s * %s', - array_get($data, 'cron_minute', '*'), - array_get($data, 'cron_hour', '*'), - array_get($data, 'cron_day_of_month', '*'), - array_get($data, 'cron_day_of_week', '*') - ); - - return CronExpression::factory($formattedCron)->getNextRunDate(); - } -} diff --git a/app/Services/Schedules/Tasks/TaskCreationService.php b/app/Services/Schedules/Tasks/TaskCreationService.php deleted file mode 100644 index 6cf4fc384..000000000 --- a/app/Services/Schedules/Tasks/TaskCreationService.php +++ /dev/null @@ -1,70 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Schedules\Tasks; - -use Webmozart\Assert\Assert; -use Pterodactyl\Models\Schedule; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; -use Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException; - -class TaskCreationService -{ - const MAX_INTERVAL_TIME_SECONDS = 900; - - /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface - */ - protected $repository; - - /** - * TaskCreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $repository - */ - public function __construct(TaskRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * Create a new task that is assigned to a schedule. - * - * @param int|\Pterodactyl\Models\Schedule $schedule - * @param array $data - * @param bool $returnModel - * @return bool|\Pterodactyl\Models\Task - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException - */ - public function handle($schedule, array $data, $returnModel = true) - { - Assert::true(($schedule instanceof Schedule || is_digit($schedule)), - 'First argument passed to handle must be numeric or instance of \Pterodactyl\Models\Schedule, received %s.' - ); - - $schedule = ($schedule instanceof Schedule) ? $schedule->id : $schedule; - $delay = $data['time_interval'] === 'm' ? $data['time_value'] * 60 : $data['time_value']; - if ($delay > self::MAX_INTERVAL_TIME_SECONDS) { - throw new TaskIntervalTooLongException(trans('exceptions.tasks.chain_interval_too_long')); - } - - $repository = ($returnModel) ? $this->repository : $this->repository->withoutFreshModel(); - $task = $repository->create([ - 'schedule_id' => $schedule, - 'sequence_id' => $data['sequence_id'], - 'action' => $data['action'], - 'payload' => $data['payload'], - 'time_offset' => $delay, - ], false); - - return $task; - } -} diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index a662cd7b9..88e48177e 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -2,182 +2,129 @@ namespace Pterodactyl\Services\Servers; +use Illuminate\Support\Arr; use Pterodactyl\Models\Server; -use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Models\Allocation; +use Illuminate\Support\Facades\Log; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class BuildModificationService { - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - private $allocationRepository; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - private $daemonServerRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - /** * BuildModificationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ public function __construct( - AllocationRepositoryInterface $allocationRepository, - ConnectionInterface $connection, - DaemonServerRepositoryInterface $daemonServerRepository, - ServerRepositoryInterface $repository + private ConnectionInterface $connection, + private DaemonServerRepository $daemonServerRepository, + private ServerConfigurationStructureService $structureService ) { - $this->allocationRepository = $allocationRepository; - $this->daemonServerRepository = $daemonServerRepository; - $this->connection = $connection; - $this->repository = $repository; } /** * Change the build details for a specified server. * - * @param \Pterodactyl\Models\Server $server - * @param array $data - * @return \Pterodactyl\Models\Server - * + * @throws \Throwable * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function handle(Server $server, array $data) + public function handle(Server $server, array $data): Server { - $build = []; - $this->connection->beginTransaction(); + /** @var \Pterodactyl\Models\Server $server */ + $server = $this->connection->transaction(function () use ($server, $data) { + $this->processAllocations($server, $data); - $this->processAllocations($server, $data); - if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) { - try { - $allocation = $this->allocationRepository->findFirstWhere([ - ['id', '=', $data['allocation_id']], - ['server_id', '=', $server->id], - ]); - } catch (RecordNotFoundException $ex) { - throw new DisplayException(trans('admin/server.exceptions.default_allocation_not_found')); + if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) { + try { + Allocation::query()->where('id', $data['allocation_id'])->where('server_id', $server->id)->firstOrFail(); + } catch (ModelNotFoundException) { + throw new DisplayException('The requested default allocation is not currently assigned to this server.'); + } } - $build['default'] = ['ip' => $allocation->ip, 'port' => $allocation->port]; - } + // If any of these values are passed through in the data array go ahead and set + // them correctly on the server model. + $merge = Arr::only($data, ['oom_killer', 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'allocation_id']); - $server = $this->repository->withFreshModel()->update($server->id, [ - 'oom_disabled' => array_get($data, 'oom_disabled'), - 'memory' => array_get($data, 'memory'), - 'swap' => array_get($data, 'swap'), - 'io' => array_get($data, 'io'), - 'cpu' => array_get($data, 'cpu'), - 'disk' => array_get($data, 'disk'), - 'allocation_id' => array_get($data, 'allocation_id'), - 'database_limit' => array_get($data, 'database_limit'), - 'allocation_limit' => array_get($data, 'allocation_limit'), - ]); + $server->forceFill(array_merge($merge, [ + 'allocation_limit' => Arr::get($data, 'allocation_limit', 0) ?? null, + 'backup_limit' => Arr::get($data, 'backup_limit', 0) ?? 0, + 'database_limit' => Arr::get($data, 'database_limit', 0) ?? null, + ]))->saveOrFail(); - $allocations = $this->allocationRepository->findWhere([['server_id', '=', $server->id]]); + return $server->refresh(); + }); - $build['oom_disabled'] = $server->oom_disabled; - $build['memory'] = (int) $server->memory; - $build['swap'] = (int) $server->swap; - $build['io'] = (int) $server->io; - $build['cpu'] = (int) $server->cpu; - $build['disk'] = (int) $server->disk; - $build['ports|overwrite'] = $allocations->groupBy('ip')->map(function ($item) { - return $item->pluck('port'); - })->toArray(); + $updateData = $this->structureService->handle($server); - try { - $this->daemonServerRepository->setServer($server)->update(['build' => $build]); - $this->connection->commit(); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); + // Because Wings always fetches an updated configuration from the Panel when booting + // a server this type of exception can be safely "ignored" and just written to the logs. + // Ideally this request succeeds, so we can apply resource modifications on the fly, but + // if it fails we can just continue on as normal. + if (!empty($updateData['build'])) { + try { + $this->daemonServerRepository->setServer($server)->sync(); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['server_id' => $server->id]); + } } return $server; } /** - * Process the allocations being assigned in the data and ensure they - * are available for a server. - * - * @param \Pterodactyl\Models\Server $server - * @param array $data + * Process the allocations being assigned in the data and ensure they are available for a server. * * @throws \Pterodactyl\Exceptions\DisplayException */ - private function processAllocations(Server $server, array &$data) + private function processAllocations(Server $server, array &$data): void { - $firstAllocationId = null; - - if (! array_key_exists('add_allocations', $data) && ! array_key_exists('remove_allocations', $data)) { + if (empty($data['add_allocations']) && empty($data['remove_allocations'])) { return; } - // Handle the addition of allocations to this server. - if (array_key_exists('add_allocations', $data) && ! empty($data['add_allocations'])) { - $unassigned = $this->allocationRepository->getUnassignedAllocationIds($server->node_id); + // Handle the addition of allocations to this server. Only assign allocations that are not currently + // assigned to a different server, and only allocations on the same node as the server. + if (!empty($data['add_allocations'])) { + $query = $server->node->allocations() + ->whereIn('id', $data['add_allocations']) + ->whereNull('server_id'); - $updateIds = []; - foreach ($data['add_allocations'] as $allocation) { - if (! in_array($allocation, $unassigned)) { - continue; - } + // Keep track of all the allocations we're just now adding so that we can use the first + // one to reset the default allocation to. + $freshlyAllocated = $query->first()->id ?? null; - $firstAllocationId = $firstAllocationId ?? $allocation; - $updateIds[] = $allocation; - } - - if (! empty($updateIds)) { - $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => $server->id]); - } + $query->update(['server_id' => $server->id, 'notes' => null]); } - // Handle removal of allocations from this server. - if (array_key_exists('remove_allocations', $data) && ! empty($data['remove_allocations'])) { - $assigned = $this->allocationRepository->getAssignedAllocationIds($server->id); - - $updateIds = []; + if (!empty($data['remove_allocations'])) { foreach ($data['remove_allocations'] as $allocation) { - if (! in_array($allocation, $assigned)) { - continue; - } - - if ($allocation == $data['allocation_id']) { - if (is_null($firstAllocationId)) { - throw new DisplayException(trans('admin/server.exceptions.no_new_default_allocation')); + // If we are attempting to remove the default allocation for the server, see if we can reassign + // to the first provided value in add_allocations. If there is no new first allocation then we + // will throw an exception back. + if ($allocation === ($data['allocation_id'] ?? $server->allocation_id)) { + if (empty($freshlyAllocated)) { + throw new DisplayException('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.'); } - $data['allocation_id'] = $firstAllocationId; + // Update the default allocation to be the first allocation that we are creating. + $data['allocation_id'] = $freshlyAllocated; } - - $updateIds[] = $allocation; } - if (! empty($updateIds)) { - $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => null]); - } + // Remove any of the allocations we got that are currently assigned to this server on + // this node. Also set the notes to null, otherwise when re-allocated to a new server those + // notes will be carried over. + Allocation::query()->where('node_id', $server->node_id) + ->where('server_id', $server->id) + // Only remove the allocations that we didn't also attempt to add to the server... + ->whereIn('id', array_diff($data['remove_allocations'], $data['add_allocations'] ?? [])) + ->update([ + 'notes' => null, + 'server_id' => null, + ]); } } } diff --git a/app/Services/Servers/ContainerRebuildService.php b/app/Services/Servers/ContainerRebuildService.php deleted file mode 100644 index 0dc25b0e4..000000000 --- a/app/Services/Servers/ContainerRebuildService.php +++ /dev/null @@ -1,42 +0,0 @@ -repository = $repository; - } - - /** - * Mark a server for rebuild on next boot cycle. - * - * @param \Pterodactyl\Models\Server $server - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - */ - public function handle(Server $server) - { - try { - $this->repository->setServer($server)->rebuild(); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } - } -} diff --git a/app/Services/Servers/DetailsModificationService.php b/app/Services/Servers/DetailsModificationService.php index 523185f2e..d135c0495 100644 --- a/app/Services/Servers/DetailsModificationService.php +++ b/app/Services/Servers/DetailsModificationService.php @@ -2,86 +2,55 @@ namespace Pterodactyl\Services\Servers; +use Illuminate\Support\Arr; use Pterodactyl\Models\Server; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Traits\Services\ReturnsUpdatedModels; -use Pterodactyl\Repositories\Eloquent\ServerRepository; -use Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService; -use Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class DetailsModificationService { use ReturnsUpdatedModels; - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService - */ - private $keyCreationService; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService - */ - private $keyDeletionService; - - /** - * @var \Pterodactyl\Repositories\Eloquent\ServerRepository - */ - private $repository; - /** * DetailsModificationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService $keyDeletionService - * @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository */ - public function __construct( - ConnectionInterface $connection, - DaemonKeyCreationService $keyCreationService, - DaemonKeyDeletionService $keyDeletionService, - ServerRepository $repository - ) { - $this->connection = $connection; - $this->keyCreationService = $keyCreationService; - $this->keyDeletionService = $keyDeletionService; - $this->repository = $repository; + public function __construct(private ConnectionInterface $connection, private DaemonServerRepository $serverRepository) + { } /** * Update the details for a single server instance. * - * @param \Pterodactyl\Models\Server $server - * @param array $data - * @return bool|\Pterodactyl\Models\Server - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function handle(Server $server, array $data) + public function handle(Server $server, array $data): Server { - $this->connection->beginTransaction(); + return $this->connection->transaction(function () use ($data, $server) { + $owner = $server->owner_id; - $response = $this->repository->setFreshModel($this->getUpdatedModel())->update($server->id, [ - 'external_id' => array_get($data, 'external_id'), - 'owner_id' => array_get($data, 'owner_id'), - 'name' => array_get($data, 'name'), - 'description' => array_get($data, 'description') ?? '', - ], true, true); + $server->forceFill([ + 'external_id' => Arr::get($data, 'external_id'), + 'owner_id' => Arr::get($data, 'owner_id'), + 'name' => Arr::get($data, 'name'), + 'description' => Arr::get($data, 'description') ?? '', + ])->saveOrFail(); - if ((int) array_get($data, 'owner_id', 0) !== (int) $server->owner_id) { - $this->keyDeletionService->handle($server, $server->owner_id); - $this->keyCreationService->handle($server->id, array_get($data, 'owner_id')); - } + // If the owner_id value is changed we need to revoke any tokens that exist for the server + // on the Wings instance so that the old owner no longer has any permission to access the + // websockets. + if ($server->owner_id !== $owner) { + try { + $this->serverRepository->setServer($server)->revokeUserJTI($owner); + } catch (DaemonConnectionException $exception) { + // Do nothing. A failure here is not ideal, but it is likely to be caused by Wings + // being offline, or in an entirely broken state. Remember, these tokens reset every + // few minutes by default, we're just trying to help it along a little quicker. + } + } - $this->connection->commit(); - - return $response; + return $server; + }); } } diff --git a/app/Services/Servers/EnvironmentService.php b/app/Services/Servers/EnvironmentService.php index b17076e07..8f45bab2d 100644 --- a/app/Services/Servers/EnvironmentService.php +++ b/app/Services/Servers/EnvironmentService.php @@ -3,54 +3,23 @@ namespace Pterodactyl\Services\Servers; use Pterodactyl\Models\Server; -use Illuminate\Contracts\Config\Repository as ConfigRepository; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Models\EggVariable; class EnvironmentService { - /** - * @var array - */ - private $additional = []; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - private $config; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - - /** - * EnvironmentService constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - */ - public function __construct(ConfigRepository $config, ServerRepositoryInterface $repository) - { - $this->config = $config; - $this->repository = $repository; - } + private array $additional = []; /** * Dynamically configure additional environment variables to be assigned * with a specific server. - * - * @param string $key - * @param callable $closure */ - public function setEnvironmentKey(string $key, callable $closure) + public function setEnvironmentKey(string $key, callable $closure): void { $this->additional[$key] = $closure; } /** * Return the dynamically added additional keys. - * - * @return array */ public function getEnvironmentKeys(): array { @@ -60,44 +29,38 @@ class EnvironmentService /** * Take all of the environment variables configured for this server and return * them in an easy to process format. - * - * @param \Pterodactyl\Models\Server $server - * @return array - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function handle(Server $server): array { - $variables = $this->repository->getVariablesWithValues($server->id); + $variables = $server->variables->toBase()->mapWithKeys(function (EggVariable $variable) { + return [$variable->env_variable => $variable->server_value ?? $variable->default_value]; + }); // Process environment variables defined in this file. This is done first // in order to allow run-time and config defined variables to take // priority over built-in values. foreach ($this->getEnvironmentMappings() as $key => $object) { - $variables[$key] = object_get($server, $object); + $variables->put($key, object_get($server, $object)); } // Process variables set in the configuration file. - foreach ($this->config->get('pterodactyl.environment_variables', []) as $key => $object) { - if (is_callable($object)) { - $variables[$key] = call_user_func($object, $server); - } else { - $variables[$key] = object_get($server, $object); - } + foreach (config('pterodactyl.environment_variables', []) as $key => $object) { + $variables->put( + $key, + is_callable($object) ? call_user_func($object, $server) : object_get($server, $object) + ); } // Process dynamically included environment variables. foreach ($this->additional as $key => $closure) { - $variables[$key] = call_user_func($closure, $server); + $variables->put($key, call_user_func($closure, $server)); } - return $variables; + return $variables->toArray(); } /** * Return a mapping of Panel default environment variables. - * - * @return array */ private function getEnvironmentMappings(): array { diff --git a/app/Services/Servers/GetUserPermissionsService.php b/app/Services/Servers/GetUserPermissionsService.php new file mode 100644 index 000000000..ae91cb33f --- /dev/null +++ b/app/Services/Servers/GetUserPermissionsService.php @@ -0,0 +1,34 @@ +root_admin || $user->id === $server->owner_id) { + $permissions = ['*']; + + if ($user->root_admin) { + $permissions[] = 'admin.websocket.errors'; + $permissions[] = 'admin.websocket.install'; + $permissions[] = 'admin.websocket.transfer'; + } + + return $permissions; + } + + /** @var \Pterodactyl\Models\Subuser|null $subuserPermissions */ + $subuserPermissions = $server->subusers()->where('user_id', $user->id)->first(); + + return $subuserPermissions ? $subuserPermissions->permissions : []; + } +} diff --git a/app/Services/Servers/ReinstallServerService.php b/app/Services/Servers/ReinstallServerService.php index 85800473f..5881d1fc6 100644 --- a/app/Services/Servers/ReinstallServerService.php +++ b/app/Services/Servers/ReinstallServerService.php @@ -1,78 +1,35 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Servers; use Pterodactyl\Models\Server; -use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; class ReinstallServerService { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonServerRepository; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $database; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $repository; - /** * ReinstallService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $database - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ public function __construct( - ConnectionInterface $database, - DaemonServerRepositoryInterface $daemonServerRepository, - ServerRepositoryInterface $repository + private ConnectionInterface $connection, + private DaemonServerRepository $daemonServerRepository ) { - $this->daemonServerRepository = $daemonServerRepository; - $this->database = $database; - $this->repository = $repository; } /** - * @param int|\Pterodactyl\Models\Server $server + * Reinstall a server on the remote daemon. * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function reinstall($server) + public function handle(Server $server): Server { - if (! $server instanceof Server) { - $server = $this->repository->find($server); - } + return $this->connection->transaction(function () use ($server) { + $server->fill(['status' => Server::STATUS_INSTALLING])->save(); - $this->database->beginTransaction(); - $this->repository->withoutFreshModel()->update($server->id, [ - 'installed' => 0, - ], true, true); - - try { $this->daemonServerRepository->setServer($server)->reinstall(); - $this->database->commit(); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } + + return $server->refresh(); + }); } } diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 46a710924..1ffbe7792 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -1,64 +1,101 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Servers; +use Pterodactyl\Models\Mount; use Pterodactyl\Models\Server; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class ServerConfigurationStructureService { - const REQUIRED_RELATIONS = ['allocation', 'allocations', 'pack', 'option']; - - /** - * @var \Pterodactyl\Services\Servers\EnvironmentService - */ - private $environment; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - /** * ServerConfigurationStructureService constructor. - * - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Pterodactyl\Services\Servers\EnvironmentService $environment */ - public function __construct( - ServerRepositoryInterface $repository, - EnvironmentService $environment - ) { - $this->repository = $repository; - $this->environment = $environment; + public function __construct(private EnvironmentService $environment) + { } /** * Return a configuration array for a specific server when passed a server model. * - * @param \Pterodactyl\Models\Server $server - * @return array - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * DO NOT MODIFY THIS FUNCTION. This powers legacy code handling for the new Wings + * daemon, if you modify the structure eggs will break unexpectedly. */ - public function handle(Server $server): array + public function handle(Server $server, array $override = [], bool $legacy = false): array { - if (array_diff(self::REQUIRED_RELATIONS, $server->getRelations())) { - $server = $this->repository->getDataForCreation($server); + $clone = $server; + // If any overrides have been set on this call make sure to update them on the + // cloned instance so that the configuration generated uses them. + if (!empty($override)) { + $clone = $server->fresh(); + foreach ($override as $key => $value) { + $clone->setAttribute($key, $value); + } } - $pack = $server->getRelation('pack'); - if (! is_null($pack)) { - $pack = $server->getRelation('pack')->uuid; - } + return $legacy + ? $this->returnLegacyFormat($clone) + : $this->returnCurrentFormat($clone); + } + /** + * Returns the new data format used for the Wings daemon. + */ + protected function returnCurrentFormat(Server $server): array + { + return [ + 'uuid' => $server->uuid, + 'meta' => [ + 'name' => $server->name, + 'description' => $server->description, + ], + 'suspended' => $server->isSuspended(), + 'environment' => $this->environment->handle($server), + 'invocation' => !is_null($server->startup) ? $server->startup : $server->egg->startup, + 'skip_egg_scripts' => $server->skip_scripts, + 'build' => [ + 'memory_limit' => $server->memory, + 'swap' => $server->swap, + 'io_weight' => $server->io, + 'cpu_limit' => $server->cpu, + 'threads' => $server->threads, + 'disk_space' => $server->disk, + // TODO: remove oom_disabled and use oom_killer, this requires a Wings update. + 'oom_disabled' => !$server->oom_killer, + 'oom_killer' => $server->oom_killer, + ], + 'container' => [ + 'image' => $server->image, + ], + 'allocations' => [ + 'default' => [ + 'ip' => $server->allocation->ip, + 'port' => $server->allocation->port, + ], + 'force_outgoing_ip' => $server->egg->force_outgoing_ip, + 'mappings' => $server->getAllocationMappings(), + ], + 'mounts' => $server->mounts->map(function (Mount $mount) { + return [ + 'source' => $mount->source, + 'target' => $mount->target, + 'read_only' => $mount->read_only, + ]; + }), + 'egg' => [ + 'id' => $server->egg->uuid, + 'file_denylist' => $server->egg->inherit_file_denylist, + ], + ]; + } + + /** + * Returns the legacy server data format to continue support for old egg configurations + * that have not yet been updated. + * + * @deprecated + */ + protected function returnLegacyFormat(Server $server): array + { return [ 'uuid' => $server->uuid, 'build' => [ @@ -70,21 +107,21 @@ class ServerConfigurationStructureService return $item->pluck('port'); })->toArray(), 'env' => $this->environment->handle($server), - 'oom_disabled' => $server->oom_disabled, + 'oom_disabled' => !$server->oom_killer, 'memory' => (int) $server->memory, 'swap' => (int) $server->swap, 'io' => (int) $server->io, 'cpu' => (int) $server->cpu, + 'threads' => $server->threads, 'disk' => (int) $server->disk, 'image' => $server->image, ], 'service' => [ 'egg' => $server->egg->uuid, - 'pack' => $pack, 'skip_scripts' => $server->skip_scripts, ], 'rebuild' => false, - 'suspended' => (int) $server->suspended, + 'suspended' => $server->isSuspended() ? 1 : 0, ]; } } diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index e9287caae..9530bd9da 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -3,111 +3,37 @@ namespace Pterodactyl\Services\Servers; use Ramsey\Uuid\Uuid; -use Pterodactyl\Models\Node; +use Illuminate\Support\Arr; +use Pterodactyl\Models\Egg; use Pterodactyl\Models\User; +use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Illuminate\Support\Collection; use Pterodactyl\Models\Allocation; -use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Models\Objects\DeploymentObject; +use Pterodactyl\Repositories\Eloquent\ServerRepository; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Services\Deployment\FindViableNodesService; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Repositories\Eloquent\ServerVariableRepository; use Pterodactyl\Services\Deployment\AllocationSelectionService; -use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class ServerCreationService { /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - private $allocationRepository; - - /** - * @var \Pterodactyl\Services\Deployment\AllocationSelectionService - */ - private $allocationSelectionService; - - /** - * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService - */ - private $configurationStructureService; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - private $daemonServerRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - private $eggRepository; - - /** - * @var \Pterodactyl\Services\Deployment\FindViableNodesService - */ - private $findViableNodesService; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface - */ - private $serverVariableRepository; - - /** - * @var \Pterodactyl\Services\Servers\VariableValidatorService - */ - private $validatorService; - - /** - * CreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository - * @param \Pterodactyl\Services\Deployment\AllocationSelectionService $allocationSelectionService - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $eggRepository - * @param \Pterodactyl\Services\Deployment\FindViableNodesService $findViableNodesService - * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository - * @param \Pterodactyl\Services\Servers\VariableValidatorService $validatorService + * ServerCreationService constructor. */ public function __construct( - AllocationRepositoryInterface $allocationRepository, - AllocationSelectionService $allocationSelectionService, - ConnectionInterface $connection, - DaemonServerRepositoryInterface $daemonServerRepository, - EggRepositoryInterface $eggRepository, - FindViableNodesService $findViableNodesService, - ServerConfigurationStructureService $configurationStructureService, - ServerRepositoryInterface $repository, - ServerVariableRepositoryInterface $serverVariableRepository, - VariableValidatorService $validatorService + private AllocationSelectionService $allocationSelectionService, + private ConnectionInterface $connection, + private DaemonServerRepository $daemonServerRepository, + private FindViableNodesService $findViableNodesService, + private ServerRepository $repository, + private ServerDeletionService $serverDeletionService, + private ServerVariableRepository $serverVariableRepository, + private VariableValidatorService $validatorService ) { - $this->allocationSelectionService = $allocationSelectionService; - $this->allocationRepository = $allocationRepository; - $this->configurationStructureService = $configurationStructureService; - $this->connection = $connection; - $this->daemonServerRepository = $daemonServerRepository; - $this->eggRepository = $eggRepository; - $this->findViableNodesService = $findViableNodesService; - $this->repository = $repository; - $this->serverVariableRepository = $serverVariableRepository; - $this->validatorService = $validatorService; } /** @@ -116,22 +42,15 @@ class ServerCreationService * as possible given the input data. For example, if an allocation_id is passed with * no node_id the node_is will be picked from the allocation. * - * @param array $data - * @param \Pterodactyl\Models\Objects\DeploymentObject|null $deployment - * @return \Pterodactyl\Models\Server - * + * @throws \Throwable * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException */ public function handle(array $data, DeploymentObject $deployment = null): Server { - $this->connection->beginTransaction(); - // If a deployment object has been passed we need to get the allocation // that the server should use, and assign the node from that allocation. if ($deployment instanceof DeploymentObject) { @@ -142,35 +61,46 @@ class ServerCreationService // Auto-configure the node based on the selected allocation // if no node was defined. - if (is_null(array_get($data, 'node_id'))) { - $data['node_id'] = $this->getNodeFromAllocation($data['allocation_id']); + if (empty($data['node_id'])) { + Assert::false(empty($data['allocation_id']), 'Expected a non-empty allocation_id in server creation data.'); + + $data['node_id'] = Allocation::query()->findOrFail($data['allocation_id'])->node_id; } - if (is_null(array_get($data, 'nest_id'))) { - $egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find(array_get($data, 'egg_id')); - $data['nest_id'] = $egg->nest_id; + if (empty($data['nest_id'])) { + Assert::false(empty($data['egg_id']), 'Expected a non-empty egg_id in server creation data.'); + + $data['nest_id'] = Egg::query()->findOrFail($data['egg_id'])->nest_id; } $eggVariableData = $this->validatorService ->setUserLevel(User::USER_LEVEL_ADMIN) - ->handle(array_get($data, 'egg_id'), array_get($data, 'environment', [])); + ->handle(Arr::get($data, 'egg_id'), Arr::get($data, 'environment', [])); - // Create the server and assign any additional allocations to it. - $server = $this->createModel($data); - $this->storeAssignedAllocations($server, $data); - $this->storeEggVariables($server, $eggVariableData); + // Due to the design of the Daemon, we need to persist this server to the disk + // before we can actually create it on the Daemon. + // + // If that connection fails out we will attempt to perform a cleanup by just + // deleting the server itself from the system. + /** @var Server $server */ + $server = $this->connection->transaction(function () use ($data, $eggVariableData) { + // Create the server and assign any additional allocations to it. + $server = $this->createModel($data); - $structure = $this->configurationStructureService->handle($server); + $this->storeAssignedAllocations($server, $data); + $this->storeEggVariables($server, $eggVariableData); + + return $server; + }, 5); try { - $this->daemonServerRepository->setServer($server)->create($structure, [ - 'start_on_completion' => (bool) array_get($data, 'start_on_completion', false), - ]); + $this->daemonServerRepository->setServer($server)->create( + Arr::get($data, 'start_on_completion', false) ?? false + ); + } catch (DaemonConnectionException $exception) { + $this->serverDeletionService->withForce()->handle($server); - $this->connection->commit(); - } catch (RequestException $exception) { - $this->connection->rollBack(); - throw new DaemonConnectionException($exception); + throw $exception; } return $server; @@ -179,23 +109,20 @@ class ServerCreationService /** * Gets an allocation to use for automatic deployment. * - * @param array $data - * @param \Pterodactyl\Models\Objects\DeploymentObject $deployment - * - * @return \Pterodactyl\Models\Allocation * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException */ private function configureDeployment(array $data, DeploymentObject $deployment): Allocation { + /** @var Collection $nodes */ $nodes = $this->findViableNodesService->setLocations($deployment->getLocations()) - ->setDisk(array_get($data, 'disk')) - ->setMemory(array_get($data, 'memory')) + ->setDisk(Arr::get($data, 'disk')) + ->setMemory(Arr::get($data, 'memory')) ->handle(); return $this->allocationSelectionService->setDedicated($deployment->isDedicated()) - ->setNodes($nodes) + ->setNodes($nodes->pluck('id')->toArray()) ->setPorts($deployment->getPorts()) ->handle(); } @@ -203,105 +130,84 @@ class ServerCreationService /** * Store the server in the database and return the model. * - * @param array $data - * @return \Pterodactyl\Models\Server - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ private function createModel(array $data): Server { $uuid = $this->generateUniqueUuidCombo(); - return $this->repository->create([ - 'external_id' => array_get($data, 'external_id'), + /** @var Server $model */ + $model = $this->repository->create([ + 'external_id' => Arr::get($data, 'external_id'), 'uuid' => $uuid, 'uuidShort' => substr($uuid, 0, 8), - 'node_id' => array_get($data, 'node_id'), - 'name' => array_get($data, 'name'), - 'description' => array_get($data, 'description') ?? '', - 'skip_scripts' => array_get($data, 'skip_scripts') ?? isset($data['skip_scripts']), - 'suspended' => false, - 'owner_id' => array_get($data, 'owner_id'), - 'memory' => array_get($data, 'memory'), - 'swap' => array_get($data, 'swap'), - 'disk' => array_get($data, 'disk'), - 'io' => array_get($data, 'io'), - 'cpu' => array_get($data, 'cpu'), - 'oom_disabled' => array_get($data, 'oom_disabled', true), - 'allocation_id' => array_get($data, 'allocation_id'), - 'nest_id' => array_get($data, 'nest_id'), - 'egg_id' => array_get($data, 'egg_id'), - 'pack_id' => (! isset($data['pack_id']) || $data['pack_id'] == 0) ? null : $data['pack_id'], - 'startup' => array_get($data, 'startup'), - 'daemonSecret' => str_random(Node::DAEMON_SECRET_LENGTH), - 'image' => array_get($data, 'image'), - 'database_limit' => array_get($data, 'database_limit'), - 'allocation_limit' => array_get($data, 'allocation_limit'), + 'node_id' => Arr::get($data, 'node_id'), + 'name' => Arr::get($data, 'name'), + 'description' => Arr::get($data, 'description') ?? '', + 'status' => Server::STATUS_INSTALLING, + 'skip_scripts' => Arr::get($data, 'skip_scripts') ?? isset($data['skip_scripts']), + 'owner_id' => Arr::get($data, 'owner_id'), + 'memory' => Arr::get($data, 'memory'), + 'swap' => Arr::get($data, 'swap'), + 'disk' => Arr::get($data, 'disk'), + 'io' => Arr::get($data, 'io'), + 'cpu' => Arr::get($data, 'cpu'), + 'threads' => Arr::get($data, 'threads'), + 'oom_killer' => Arr::get($data, 'oom_killer') ?? false, + 'allocation_id' => Arr::get($data, 'allocation_id'), + 'nest_id' => Arr::get($data, 'nest_id'), + 'egg_id' => Arr::get($data, 'egg_id'), + 'startup' => Arr::get($data, 'startup'), + 'image' => Arr::get($data, 'image'), + 'database_limit' => Arr::get($data, 'database_limit') ?? 0, + 'allocation_limit' => Arr::get($data, 'allocation_limit') ?? 0, + 'backup_limit' => Arr::get($data, 'backup_limit') ?? 0, ]); + + return $model; } /** * Configure the allocations assigned to this server. - * - * @param \Pterodactyl\Models\Server $server - * @param array $data */ - private function storeAssignedAllocations(Server $server, array $data) + private function storeAssignedAllocations(Server $server, array $data): void { $records = [$data['allocation_id']]; if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) { $records = array_merge($records, $data['allocation_additional']); } - $this->allocationRepository->assignAllocationsToServer($server->id, $records); + Allocation::query()->whereIn('id', $records)->update([ + 'server_id' => $server->id, + ]); } /** * Process environment variables passed for this server and store them in the database. - * - * @param \Pterodactyl\Models\Server $server - * @param \Illuminate\Support\Collection $variables */ - private function storeEggVariables(Server $server, Collection $variables) + private function storeEggVariables(Server $server, Collection $variables): void { $records = $variables->map(function ($result) use ($server) { return [ 'server_id' => $server->id, 'variable_id' => $result->id, - 'variable_value' => $result->value, + 'variable_value' => $result->value ?? '', ]; })->toArray(); - if (! empty($records)) { + if (!empty($records)) { $this->serverVariableRepository->insert($records); } } - /** - * Get the node that an allocation belongs to. - * - * @param int $allocation - * @return int - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - private function getNodeFromAllocation(int $allocation): int - { - $allocation = $this->allocationRepository->setColumns(['id', 'node_id'])->find($allocation); - - return $allocation->node_id; - } - /** * Create a unique UUID and UUID-Short combo for a server. - * - * @return string */ private function generateUniqueUuidCombo(): string { $uuid = Uuid::uuid4()->toString(); - if (! $this->repository->isUniqueUuidCombo($uuid, substr($uuid, 0, 8))) { + if (!$this->repository->isUniqueUuidCombo($uuid, substr($uuid, 0, 8))) { return $this->generateUniqueUuidCombo(); } diff --git a/app/Services/Servers/ServerDeletionService.php b/app/Services/Servers/ServerDeletionService.php index faed3b9ff..a83c8feb6 100644 --- a/app/Services/Servers/ServerDeletionService.php +++ b/app/Services/Servers/ServerDeletionService.php @@ -1,93 +1,33 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Servers; -use Psr\Log\LoggerInterface as Writer; -use GuzzleHttp\Exception\RequestException; +use Illuminate\Http\Response; +use Pterodactyl\Models\Server; +use Illuminate\Support\Facades\Log; use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Services\Databases\DatabaseManagementService; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class ServerDeletionService { - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; + protected bool $force = false; /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonServerRepository; - - /** - * @var \Pterodactyl\Services\Databases\DatabaseManagementService - */ - protected $databaseManagementService; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - protected $databaseRepository; - - /** - * @var bool - */ - protected $force = false; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $repository; - - /** - * @var \Psr\Log\LoggerInterface - */ - protected $writer; - - /** - * DeletionService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository - * @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Psr\Log\LoggerInterface $writer + * ServerDeletionService constructor. */ public function __construct( - ConnectionInterface $connection, - DaemonServerRepositoryInterface $daemonServerRepository, - DatabaseRepositoryInterface $databaseRepository, - DatabaseManagementService $databaseManagementService, - ServerRepositoryInterface $repository, - Writer $writer + private ConnectionInterface $connection, + private DaemonServerRepository $daemonServerRepository, + private DatabaseManagementService $databaseManagementService ) { - $this->daemonServerRepository = $daemonServerRepository; - $this->connection = $connection; - $this->databaseManagementService = $databaseManagementService; - $this->databaseRepository = $databaseRepository; - $this->repository = $repository; - $this->writer = $writer; } /** * Set if the server should be forcibly deleted from the panel (ignoring daemon errors) or not. - * - * @param bool $bool - * @return $this */ - public function withForce($bool = true) + public function withForce(bool $bool = true): self { $this->force = $bool; @@ -97,34 +37,47 @@ class ServerDeletionService /** * Delete a server from the panel and remove any associated databases from hosts. * - * @param int|\Pterodactyl\Models\Server $server - * + * @throws \Throwable * @throws \Pterodactyl\Exceptions\DisplayException */ - public function handle($server) + public function handle(Server $server): void { try { $this->daemonServerRepository->setServer($server)->delete(); - } catch (RequestException $exception) { - $response = $exception->getResponse(); - - if (is_null($response) || (! is_null($response) && $response->getStatusCode() !== 404)) { - // If not forcing the deletion, throw an exception, otherwise just log it and - // continue with server deletion process in the panel. - if (! $this->force) { - throw new DaemonConnectionException($exception); - } else { - $this->writer->warning($exception); - } + } catch (DaemonConnectionException $exception) { + // If there is an error not caused a 404 error and this isn't a forced delete, + // go ahead and bail out. We specifically ignore a 404 since that can be assumed + // to be a safe error, meaning the server doesn't exist at all on Wings so there + // is no reason we need to bail out from that. + if (!$this->force && $exception->getStatusCode() !== Response::HTTP_NOT_FOUND) { + throw $exception; } + + Log::warning($exception); } - $this->connection->beginTransaction(); - $this->databaseRepository->setColumns('id')->findWhere([['server_id', '=', $server->id]])->each(function ($item) { - $this->databaseManagementService->delete($item->id); - }); + $this->connection->transaction(function () use ($server) { + foreach ($server->databases as $database) { + try { + $this->databaseManagementService->delete($database); + } catch (\Exception $exception) { + if (!$this->force) { + throw $exception; + } - $this->repository->delete($server->id); - $this->connection->commit(); + // Oh well, just try to delete the database entry we have from the database + // so that the server itself can be deleted. This will leave it dangling on + // the host instance, but we couldn't delete it anyways so not sure how we would + // handle this better anyways. + // + // @see https://github.com/pterodactyl/panel/issues/2085 + $database->delete(); + + Log::warning($exception); + } + } + + $server->delete(); + }); } } diff --git a/app/Services/Servers/StartupCommandService.php b/app/Services/Servers/StartupCommandService.php new file mode 100644 index 000000000..efdbbc5c4 --- /dev/null +++ b/app/Services/Servers/StartupCommandService.php @@ -0,0 +1,24 @@ +memory, $server->allocation->ip, $server->allocation->port]; + + foreach ($server->variables as $variable) { + $find[] = '{{' . $variable->env_variable . '}}'; + $replace[] = ($variable->user_viewable && !$hideAllValues) ? ($variable->server_value ?? $variable->default_value) : '[hidden]'; + } + + return str_replace($find, $replace, $server->startup); + } +} diff --git a/app/Services/Servers/StartupCommandViewService.php b/app/Services/Servers/StartupCommandViewService.php deleted file mode 100644 index d3cda3143..000000000 --- a/app/Services/Servers/StartupCommandViewService.php +++ /dev/null @@ -1,56 +0,0 @@ -repository = $repository; - } - - /** - * Generate a startup command for a server and return all of the user-viewable variables - * as well as their assigned values. - * - * @param int $server - * @return \Illuminate\Support\Collection - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(int $server): Collection - { - $response = $this->repository->getVariablesWithValues($server, true); - $server = $this->repository->getPrimaryAllocation($response->server); - - $find = ['{{SERVER_MEMORY}}', '{{SERVER_IP}}', '{{SERVER_PORT}}']; - $replace = [$server->memory, $server->getRelation('allocation')->ip, $server->getRelation('allocation')->port]; - - $variables = $server->getRelation('egg')->getRelation('variables') - ->each(function ($variable) use (&$find, &$replace, $response) { - $find[] = '{{' . $variable->env_variable . '}}'; - $replace[] = $variable->user_viewable ? $response->data[$variable->env_variable] : '[hidden]'; - })->filter(function ($variable) { - return $variable->user_viewable === 1; - }); - - return collect([ - 'startup' => str_replace($find, $replace, $server->startup), - 'variables' => $variables, - 'server_values' => $response->data, - ]); - } -} diff --git a/app/Services/Servers/StartupModificationService.php b/app/Services/Servers/StartupModificationService.php index 9fa1390db..a0a5c4cb7 100644 --- a/app/Services/Servers/StartupModificationService.php +++ b/app/Services/Servers/StartupModificationService.php @@ -2,174 +2,87 @@ namespace Pterodactyl\Services\Servers; +use Illuminate\Support\Arr; +use Pterodactyl\Models\Egg; use Pterodactyl\Models\User; use Pterodactyl\Models\Server; -use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Models\ServerVariable; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Traits\Services\HasUserLevels; -use Pterodactyl\Contracts\Repository\EggRepositoryInterface; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class StartupModificationService { use HasUserLevels; - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - private $daemonServerRepository; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface - */ - private $eggRepository; - - /** - * @var \Pterodactyl\Services\Servers\EnvironmentService - */ - private $environmentService; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface - */ - private $serverVariableRepository; - - /** - * @var \Pterodactyl\Services\Servers\VariableValidatorService - */ - private $validatorService; - /** * StartupModificationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $eggRepository - * @param \Pterodactyl\Services\Servers\EnvironmentService $environmentService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository - * @param \Pterodactyl\Services\Servers\VariableValidatorService $validatorService */ - public function __construct( - ConnectionInterface $connection, - DaemonServerRepositoryInterface $daemonServerRepository, - EggRepositoryInterface $eggRepository, - EnvironmentService $environmentService, - ServerRepositoryInterface $repository, - ServerVariableRepositoryInterface $serverVariableRepository, - VariableValidatorService $validatorService - ) { - $this->daemonServerRepository = $daemonServerRepository; - $this->connection = $connection; - $this->eggRepository = $eggRepository; - $this->environmentService = $environmentService; - $this->repository = $repository; - $this->serverVariableRepository = $serverVariableRepository; - $this->validatorService = $validatorService; + public function __construct(private ConnectionInterface $connection, private VariableValidatorService $validatorService) + { } /** * Process startup modification for a server. * - * @param \Pterodactyl\Models\Server $server - * @param array $data - * @return \Pterodactyl\Models\Server - * - * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ public function handle(Server $server, array $data): Server { - $this->connection->beginTransaction(); - if (! is_null(array_get($data, 'environment'))) { - $this->validatorService->setUserLevel($this->getUserLevel()); - $results = $this->validatorService->handle(array_get($data, 'egg_id', $server->egg_id), array_get($data, 'environment', [])); + return $this->connection->transaction(function () use ($server, $data) { + if (!empty($data['environment'])) { + $egg = $this->isUserLevel(User::USER_LEVEL_ADMIN) ? ($data['egg_id'] ?? $server->egg_id) : $server->egg_id; - $results->each(function ($result) use ($server) { - $this->serverVariableRepository->withoutFreshModel()->updateOrCreate([ - 'server_id' => $server->id, - 'variable_id' => $result->id, - ], [ - 'variable_value' => $result->value ?? '', - ]); - }); - } + $results = $this->validatorService + ->setUserLevel($this->getUserLevel()) + ->handle($egg, $data['environment']); - $daemonData = []; - if ($this->isUserLevel(User::USER_LEVEL_ADMIN)) { - $this->updateAdministrativeSettings($data, $server, $daemonData); - } + foreach ($results as $result) { + ServerVariable::query()->updateOrCreate( + [ + 'server_id' => $server->id, + 'variable_id' => $result->id, + ], + ['variable_value' => $result->value ?? ''] + ); + } + } - $daemonData = array_merge_recursive($daemonData, [ - 'build' => [ - 'env|overwrite' => $this->environmentService->handle($server), - ], - ]); + if ($this->isUserLevel(User::USER_LEVEL_ADMIN)) { + $this->updateAdministrativeSettings($data, $server); + } - try { - $this->daemonServerRepository->setServer($server)->update($daemonData); - } catch (RequestException $exception) { - $this->connection->rollBack(); - throw new DaemonConnectionException($exception); - } - - $this->connection->commit(); - - return $server; + // Calling ->refresh() rather than ->fresh() here causes it to return the + // variables as triplicates for some reason? Not entirely sure, should dig + // in more to figure it out, but luckily we have a test case covering this + // specific call so we can be assured we're not breaking it _here_ at least. + // + // TODO(dane): this seems like a red-flag for the code powering the relationship + // that should be looked into more. + return $server->fresh(); + }); } /** * Update certain administrative settings for a server in the DB. - * - * @param array $data - * @param \Pterodactyl\Models\Server $server - * @param array $daemonData - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - private function updateAdministrativeSettings(array $data, Server &$server, array &$daemonData) + protected function updateAdministrativeSettings(array $data, Server &$server): void { - if ( - is_digit(array_get($data, 'egg_id')) - && $data['egg_id'] != $server->egg_id - && is_null(array_get($data, 'nest_id')) - ) { - $egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find($data['egg_id']); - $data['nest_id'] = $egg->nest_id; + $eggId = Arr::get($data, 'egg_id'); + + if (is_digit($eggId) && $server->egg_id !== (int) $eggId) { + /** @var \Pterodactyl\Models\Egg $egg */ + $egg = Egg::query()->findOrFail($data['egg_id']); + + $server = $server->forceFill([ + 'egg_id' => $egg->id, + 'nest_id' => $egg->nest_id, + ]); } - $server = $this->repository->update($server->id, [ - 'installed' => 0, - 'startup' => array_get($data, 'startup', $server->startup), - 'nest_id' => array_get($data, 'nest_id', $server->nest_id), - 'egg_id' => array_get($data, 'egg_id', $server->egg_id), - 'pack_id' => array_get($data, 'pack_id', $server->pack_id) > 0 ? array_get($data, 'pack_id', $server->pack_id) : null, - 'skip_scripts' => array_get($data, 'skip_scripts') ?? isset($data['skip_scripts']), - 'image' => array_get($data, 'docker_image', $server->image), - ]); - - $daemonData = array_merge($daemonData, [ - 'build' => ['image' => $server->image], - 'service' => array_merge( - $this->repository->getDaemonServiceData($server, true), - ['skip_scripts' => $server->skip_scripts] - ), - ]); + $server->fill([ + 'startup' => $data['startup'] ?? $server->startup, + 'skip_scripts' => $data['skip_scripts'] ?? isset($data['skip_scripts']), + 'image' => $data['docker_image'] ?? $server->image, + ])->save(); } } diff --git a/app/Services/Servers/SuspensionService.php b/app/Services/Servers/SuspensionService.php index d8d367803..d11ace22c 100644 --- a/app/Services/Servers/SuspensionService.php +++ b/app/Services/Servers/SuspensionService.php @@ -1,115 +1,61 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Servers; +use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; -use Psr\Log\LoggerInterface as Writer; -use GuzzleHttp\Exception\RequestException; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; class SuspensionService { - const ACTION_SUSPEND = 'suspend'; - const ACTION_UNSUSPEND = 'unsuspend'; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonServerRepository; - - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $database; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $repository; - - /** - * @var \Psr\Log\LoggerInterface - */ - protected $writer; + public const ACTION_SUSPEND = 'suspend'; + public const ACTION_UNSUSPEND = 'unsuspend'; /** * SuspensionService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $database - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Psr\Log\LoggerInterface $writer */ public function __construct( - ConnectionInterface $database, - DaemonServerRepositoryInterface $daemonServerRepository, - ServerRepositoryInterface $repository, - Writer $writer + private DaemonServerRepository $daemonServerRepository ) { - $this->daemonServerRepository = $daemonServerRepository; - $this->database = $database; - $this->repository = $repository; - $this->writer = $writer; } /** * Suspends a server on the system. * - * @param int|\Pterodactyl\Models\Server $server - * @param string $action - * @return bool - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function toggle($server, $action = self::ACTION_SUSPEND) + public function toggle(Server $server, string $action = self::ACTION_SUSPEND): void { - if (! $server instanceof Server) { - $server = $this->repository->find($server); + Assert::oneOf($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND]); + + $isSuspending = $action === self::ACTION_SUSPEND; + // Nothing needs to happen if we're suspending the server, and it is already + // suspended in the database. Additionally, nothing needs to happen if the server + // is not suspended, and we try to un-suspend the instance. + if ($isSuspending === $server->isSuspended()) { + return; } - if (! in_array($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND])) { - throw new \InvalidArgumentException(sprintf( - 'Action must be either ' . self::ACTION_SUSPEND . ' or ' . self::ACTION_UNSUSPEND . ', %s passed.', - $action - )); + // Check if the server is currently being transferred. + if (!is_null($server->transfer)) { + throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.'); } - if ( - $action === self::ACTION_SUSPEND && $server->suspended || - $action === self::ACTION_UNSUSPEND && ! $server->suspended - ) { - return true; - } - - $this->database->beginTransaction(); - $this->repository->withoutFreshModel()->update($server->id, [ - 'suspended' => $action === self::ACTION_SUSPEND, + // Update the server's suspension status. + $server->update([ + 'status' => $isSuspending ? Server::STATUS_SUSPENDED : null, ]); try { - $this->daemonServerRepository->setServer($server)->$action(); - $this->database->commit(); - - return true; - } catch (RequestException $exception) { - $response = $exception->getResponse(); - $this->writer->warning($exception); - - throw new DisplayException(trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ])); + // Tell wings to re-sync the server state. + $this->daemonServerRepository->setServer($server)->sync(); + } catch (\Exception $exception) { + // Rollback the server's suspension status if wings fails to sync the server. + $server->update([ + 'status' => $isSuspending ? null : Server::STATUS_SUSPENDED, + ]); + throw $exception; } } } diff --git a/app/Services/Servers/VariableValidatorService.php b/app/Services/Servers/VariableValidatorService.php index 758460559..a19869265 100644 --- a/app/Services/Servers/VariableValidatorService.php +++ b/app/Services/Servers/VariableValidatorService.php @@ -1,87 +1,44 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Servers; use Pterodactyl\Models\User; use Illuminate\Support\Collection; +use Pterodactyl\Models\EggVariable; use Illuminate\Validation\ValidationException; use Pterodactyl\Traits\Services\HasUserLevels; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Illuminate\Contracts\Validation\Factory as ValidationFactory; -use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; class VariableValidatorService { use HasUserLevels; - /** - * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface - */ - private $optionVariableRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $serverRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface - */ - private $serverVariableRepository; - - /** - * @var \Illuminate\Contracts\Validation\Factory - */ - private $validator; - /** * VariableValidatorService constructor. - * - * @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $optionVariableRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository - * @param \Illuminate\Contracts\Validation\Factory $validator */ - public function __construct( - EggVariableRepositoryInterface $optionVariableRepository, - ServerRepositoryInterface $serverRepository, - ServerVariableRepositoryInterface $serverVariableRepository, - ValidationFactory $validator - ) { - $this->optionVariableRepository = $optionVariableRepository; - $this->serverRepository = $serverRepository; - $this->serverVariableRepository = $serverVariableRepository; - $this->validator = $validator; + public function __construct(private ValidationFactory $validator) + { } /** * Validate all of the passed data against the given service option variables. * - * @param int $egg - * @param array $fields - * @return \Illuminate\Support\Collection * @throws \Illuminate\Validation\ValidationException */ public function handle(int $egg, array $fields = []): Collection { - $variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]); + $query = EggVariable::query()->where('egg_id', $egg); + if (!$this->isUserLevel(User::USER_LEVEL_ADMIN)) { + // Don't attempt to validate variables if they aren't user editable, + // and we're not running this at an admin level. + $query = $query->where('user_editable', true)->where('user_viewable', true); + } + + /** @var \Pterodactyl\Models\EggVariable[] $variables */ + $variables = $query->get(); $data = $rules = $customAttributes = []; foreach ($variables as $variable) { - // Don't attempt to validate variables if they aren't user editable - // and we're not running this at an admin level. - if (! $variable->user_editable && ! $this->isUserLevel(User::USER_LEVEL_ADMIN)) { - continue; - } - $data['environment'][$variable->env_variable] = array_get($fields, $variable->env_variable); $rules['environment.' . $variable->env_variable] = $variable->rules; $customAttributes['environment.' . $variable->env_variable] = trans('validation.internal.variable_value', ['env' => $variable->name]); @@ -92,23 +49,12 @@ class VariableValidatorService throw new ValidationException($validator); } - $response = $variables->filter(function ($item) { - // Skip doing anything if user is not an admin and variable is not user viewable or editable. - if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) { - return false; - } - - return true; - })->map(function ($item) use ($fields) { + return Collection::make($variables)->map(function ($item) use ($fields) { return (object) [ 'id' => $item->id, 'key' => $item->env_variable, - 'value' => array_get($fields, $item->env_variable), + 'value' => $fields[$item->env_variable] ?? null, ]; - })->filter(function ($item) { - return is_object($item); }); - - return $response; } } diff --git a/app/Services/Sftp/AuthenticateUsingPasswordService.php b/app/Services/Sftp/AuthenticateUsingPasswordService.php deleted file mode 100644 index 4310a50f6..000000000 --- a/app/Services/Sftp/AuthenticateUsingPasswordService.php +++ /dev/null @@ -1,108 +0,0 @@ -keyProviderService = $keyProviderService; - $this->repository = $repository; - $this->subuserRepository = $subuserRepository; - $this->userRepository = $userRepository; - } - - /** - * Attempt to authenticate a provided username and password and determine if they - * have permission to access a given server. This function does not account for - * subusers currently. Only administrators and server owners can login to access - * their files at this time. - * - * Server must exist on the node that the API call is being made from in order for a - * valid response to be provided. - * - * @param string $username - * @param string $password - * @param int $node - * @param string|null $server - * @return array - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException - */ - public function handle(string $username, string $password, int $node, string $server = null): array - { - if (is_null($server)) { - throw new RecordNotFoundException; - } - - $user = $this->userRepository->setColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]); - if (! password_verify($password, $user->password)) { - throw new RecordNotFoundException; - } - - $server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->getByUuid($server); - if ($server->node_id !== $node) { - throw new RecordNotFoundException; - } - - if (! $user->root_admin && $server->owner_id !== $user->id) { - $subuser = $this->subuserRepository->getWithPermissionsUsingUserAndServer($user->id, $server->id); - $permissions = $subuser->getRelation('permissions')->pluck('permission')->toArray(); - - if (! in_array('access-sftp', $permissions)) { - throw new RecordNotFoundException; - } - } - - if ($server->installed !== 1 || $server->suspended) { - throw new BadRequestHttpException; - } - - return [ - 'server' => $server->uuid, - 'token' => $this->keyProviderService->handle($server, $user), - 'permissions' => $permissions ?? ['*'], - ]; - } -} diff --git a/app/Services/Subusers/PermissionCreationService.php b/app/Services/Subusers/PermissionCreationService.php deleted file mode 100644 index 074048f0a..000000000 --- a/app/Services/Subusers/PermissionCreationService.php +++ /dev/null @@ -1,63 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Subusers; - -use Webmozart\Assert\Assert; -use Pterodactyl\Models\Permission; -use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; - -class PermissionCreationService -{ - /** - * @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface - */ - protected $repository; - - /** - * PermissionCreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface $repository - */ - public function __construct(PermissionRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * Assign permissions to a given subuser. - * - * @param int $subuser - * @param array $permissions - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function handle($subuser, array $permissions) - { - Assert::integerish($subuser, 'First argument passed to handle must be an integer, received %s.'); - - $permissionMappings = Permission::getPermissions(true); - $insertPermissions = []; - - foreach ($permissions as $permission) { - if (array_key_exists($permission, $permissionMappings)) { - Assert::stringNotEmpty($permission, 'Permission argument provided must be a non-empty string, received %s.'); - - array_push($insertPermissions, [ - 'subuser_id' => $subuser, - 'permission' => $permission, - ]); - } - } - - if (! empty($insertPermissions)) { - $this->repository->withoutFreshModel()->insert($insertPermissions); - } - } -} diff --git a/app/Services/Subusers/SubuserCreationService.php b/app/Services/Subusers/SubuserCreationService.php index 1a5d7120f..e45f2813a 100644 --- a/app/Services/Subusers/SubuserCreationService.php +++ b/app/Services/Subusers/SubuserCreationService.php @@ -1,137 +1,72 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Subusers; +use Illuminate\Support\Str; use Pterodactyl\Models\Server; +use Pterodactyl\Models\Subuser; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Services\Users\UserCreationService; +use Pterodactyl\Repositories\Eloquent\SubuserRepository; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; use Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException; use Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException; class SubuserCreationService { - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService - */ - protected $keyCreationService; - - /** - * @var \Pterodactyl\Services\Subusers\PermissionCreationService - */ - protected $permissionService; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface - */ - protected $subuserRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * @var \Pterodactyl\Services\Users\UserCreationService - */ - protected $userCreationService; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - protected $userRepository; - /** * SubuserCreationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService - * @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository - * @param \Pterodactyl\Services\Users\UserCreationService $userCreationService - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository */ public function __construct( - ConnectionInterface $connection, - DaemonKeyCreationService $keyCreationService, - PermissionCreationService $permissionService, - ServerRepositoryInterface $serverRepository, - SubuserRepositoryInterface $subuserRepository, - UserCreationService $userCreationService, - UserRepositoryInterface $userRepository + private ConnectionInterface $connection, + private SubuserRepository $subuserRepository, + private UserCreationService $userCreationService, + private UserRepositoryInterface $userRepository ) { - $this->connection = $connection; - $this->keyCreationService = $keyCreationService; - $this->permissionService = $permissionService; - $this->serverRepository = $serverRepository; - $this->subuserRepository = $subuserRepository; - $this->userRepository = $userRepository; - $this->userCreationService = $userCreationService; } /** - * @param int|\Pterodactyl\Models\Server $server - * @param string $email - * @param array $permissions - * @return \Pterodactyl\Models\Subuser + * Creates a new user on the system and assigns them access to the provided server. + * If the email address already belongs to a user on the system a new user will not + * be created. * - * @throws \Exception * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException + * @throws \Throwable */ - public function handle($server, $email, array $permissions) + public function handle(Server $server, string $email, array $permissions): Subuser { - if (! $server instanceof Server) { - $server = $this->serverRepository->find($server); - } + return $this->connection->transaction(function () use ($server, $email, $permissions) { + try { + $user = $this->userRepository->findFirstWhere([['email', '=', $email]]); - $this->connection->beginTransaction(); - try { - $user = $this->userRepository->findFirstWhere([['email', '=', $email]]); + if ($server->owner_id === $user->id) { + throw new UserIsServerOwnerException(trans('exceptions.subusers.user_is_owner')); + } - if ($server->owner_id === $user->id) { - throw new UserIsServerOwnerException(trans('exceptions.subusers.user_is_owner')); + $subuserCount = $this->subuserRepository->findCountWhere([['user_id', '=', $user->id], ['server_id', '=', $server->id]]); + if ($subuserCount !== 0) { + throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists')); + } + } catch (RecordNotFoundException) { + // Just cap the username generated at 64 characters at most and then append a random string + // to the end to make it "unique"... + $username = substr(preg_replace('/([^\w\.-]+)/', '', strtok($email, '@')), 0, 64) . Str::random(3); + + $user = $this->userCreationService->handle([ + 'email' => $email, + 'username' => $username, + 'root_admin' => false, + ]); } - $subuserCount = $this->subuserRepository->findCountWhere([['user_id', '=', $user->id], ['server_id', '=', $server->id]]); - if ($subuserCount !== 0) { - throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists')); - } - } catch (RecordNotFoundException $exception) { - $username = preg_replace('/([^\w\.-]+)/', '', strtok($email, '@')); - $user = $this->userCreationService->handle([ - 'email' => $email, - 'username' => $username . str_random(3), - 'name_first' => 'Server', - 'name_last' => 'Subuser', - 'root_admin' => false, + return $this->subuserRepository->create([ + 'user_id' => $user->id, + 'server_id' => $server->id, + 'permissions' => array_unique($permissions), ]); - } - - $subuser = $this->subuserRepository->create(['user_id' => $user->id, 'server_id' => $server->id]); - $this->keyCreationService->handle($server->id, $user->id); - $this->permissionService->handle($subuser->id, $permissions); - $this->connection->commit(); - - return $subuser; + }); } } diff --git a/app/Services/Subusers/SubuserDeletionService.php b/app/Services/Subusers/SubuserDeletionService.php deleted file mode 100644 index 6b100cd18..000000000 --- a/app/Services/Subusers/SubuserDeletionService.php +++ /dev/null @@ -1,66 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Subusers; - -use Pterodactyl\Models\Subuser; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; - -class SubuserDeletionService -{ - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService - */ - private $keyDeletionService; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface - */ - private $repository; - - /** - * SubuserDeletionService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService $keyDeletionService - * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository - */ - public function __construct( - ConnectionInterface $connection, - DaemonKeyDeletionService $keyDeletionService, - SubuserRepositoryInterface $repository - ) { - $this->connection = $connection; - $this->keyDeletionService = $keyDeletionService; - $this->repository = $repository; - } - - /** - * Delete a subuser and their associated permissions from the Panel and Daemon. - * - * @param \Pterodactyl\Models\Subuser $subuser - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(Subuser $subuser) - { - $this->connection->beginTransaction(); - $this->keyDeletionService->handle($subuser->server_id, $subuser->user_id); - $this->repository->delete($subuser->id); - $this->connection->commit(); - } -} diff --git a/app/Services/Subusers/SubuserUpdateService.php b/app/Services/Subusers/SubuserUpdateService.php deleted file mode 100644 index f56e47b9d..000000000 --- a/app/Services/Subusers/SubuserUpdateService.php +++ /dev/null @@ -1,107 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Subusers; - -use Pterodactyl\Models\Subuser; -use GuzzleHttp\Exception\RequestException; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; -use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; - -class SubuserUpdateService -{ - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - private $daemonRepository; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService - */ - private $keyProviderService; - - /** - * @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface - */ - private $permissionRepository; - - /** - * @var \Pterodactyl\Services\Subusers\PermissionCreationService - */ - private $permissionService; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface - */ - private $repository; - - /** - * SubuserUpdateService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository - * @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService - * @param \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface $permissionRepository - * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository - */ - public function __construct( - ConnectionInterface $connection, - DaemonKeyProviderService $keyProviderService, - DaemonServerRepositoryInterface $daemonRepository, - PermissionCreationService $permissionService, - PermissionRepositoryInterface $permissionRepository, - SubuserRepositoryInterface $repository - ) { - $this->connection = $connection; - $this->daemonRepository = $daemonRepository; - $this->keyProviderService = $keyProviderService; - $this->permissionRepository = $permissionRepository; - $this->permissionService = $permissionService; - $this->repository = $repository; - } - - /** - * Update permissions for a given subuser. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @param array $permissions - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(Subuser $subuser, array $permissions) - { - $subuser = $this->repository->loadServerAndUserRelations($subuser); - - $this->connection->beginTransaction(); - $this->permissionRepository->deleteWhere([['subuser_id', '=', $subuser->id]]); - $this->permissionService->handle($subuser->id, $permissions); - - try { - $token = $this->keyProviderService->handle($subuser->getRelation('server'), $subuser->getRelation('user'), false); - $this->daemonRepository->setServer($subuser->getRelation('server'))->revokeAccessKey($token); - } catch (RequestException $exception) { - $this->connection->rollBack(); - throw new DaemonConnectionException($exception); - } - - $this->connection->commit(); - } -} diff --git a/app/Services/Telemetry/TelemetryCollectionService.php b/app/Services/Telemetry/TelemetryCollectionService.php new file mode 100644 index 000000000..016cf0817 --- /dev/null +++ b/app/Services/Telemetry/TelemetryCollectionService.php @@ -0,0 +1,189 @@ +collect(); + } catch (Exception) { + return; + } + + Http::post('https://telemetry.pterodactyl.io', $data); + } + + /** + * Collects telemetry data and returns it as an array. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function collect(): array + { + $uuid = $this->settingsRepository->get('app:telemetry:uuid'); + if (is_null($uuid)) { + $uuid = Uuid::uuid4()->toString(); + $this->settingsRepository->set('app:telemetry:uuid', $uuid); + } + + $nodes = Node::all()->map(function ($node) { + try { + $info = $this->daemonConfigurationRepository->setNode($node)->getSystemInformation(2); + } catch (Exception) { + return null; + } + + return [ + 'id' => $node->uuid, + 'version' => Arr::get($info, 'version', ''), + + 'docker' => [ + 'version' => Arr::get($info, 'docker.version', ''), + + 'cgroups' => [ + 'driver' => Arr::get($info, 'docker.cgroups.driver', ''), + 'version' => Arr::get($info, 'docker.cgroups.version', ''), + ], + + 'containers' => [ + 'total' => Arr::get($info, 'docker.containers.total', -1), + 'running' => Arr::get($info, 'docker.containers.running', -1), + 'paused' => Arr::get($info, 'docker.containers.paused', -1), + 'stopped' => Arr::get($info, 'docker.containers.stopped', -1), + ], + + 'storage' => [ + 'driver' => Arr::get($info, 'docker.storage.driver', ''), + 'filesystem' => Arr::get($info, 'docker.storage.filesystem', ''), + ], + + 'runc' => [ + 'version' => Arr::get($info, 'docker.runc.version', ''), + ], + ], + + 'system' => [ + 'architecture' => Arr::get($info, 'system.architecture', ''), + 'cpuThreads' => Arr::get($info, 'system.cpu_threads', ''), + 'memoryBytes' => Arr::get($info, 'system.memory_bytes', ''), + 'kernelVersion' => Arr::get($info, 'system.kernel_version', ''), + 'os' => Arr::get($info, 'system.os', ''), + 'osType' => Arr::get($info, 'system.os_type', ''), + ], + ]; + })->filter(fn ($node) => !is_null($node))->toArray(); + + return [ + 'id' => $uuid, + + 'panel' => [ + 'version' => $this->softwareVersionService->getCurrentVersion(), + 'phpVersion' => phpversion(), + + 'drivers' => [ + 'backup' => [ + 'type' => config('backups.default'), + ], + + 'cache' => [ + 'type' => config('cache.default'), + ], + + 'database' => [ + 'type' => config('database.default'), + 'version' => DB::getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION), + ], + ], + ], + + 'resources' => [ + 'allocations' => [ + 'count' => Allocation::count(), + 'used' => Allocation::whereNotNull('server_id')->count(), + ], + + 'backups' => [ + 'count' => Backup::count(), + 'bytes' => Backup::sum('bytes'), + ], + + 'eggs' => [ + 'count' => Egg::count(), + // Egg UUIDs are generated randomly on import, so there is not a consistent way to + // determine if servers are using default eggs or not. +// 'server_usage' => Egg::all() +// ->flatMap(fn (Egg $egg) => [$egg->uuid => $egg->servers->count()]) +// ->filter(fn (int $count) => $count > 0) +// ->toArray(), + ], + + 'locations' => [ + 'count' => Location::count(), + ], + + 'mounts' => [ + 'count' => Mount::count(), + ], + + 'nests' => [ + 'count' => Nest::count(), + // Nest UUIDs are generated randomly on import, so there is not a consistent way to + // determine if servers are using default eggs or not. +// 'server_usage' => Nest::all() +// ->flatMap(fn (Nest $nest) => [$nest->uuid => $nest->eggs->sum(fn (Egg $egg) => $egg->servers->count())]) +// ->filter(fn (int $count) => $count > 0) +// ->toArray(), + ], + + 'nodes' => [ + 'count' => Node::count(), + ], + + 'servers' => [ + 'count' => Server::count(), + 'suspended' => Server::where('status', Server::STATUS_SUSPENDED)->count(), + ], + + 'users' => [ + 'count' => User::count(), + 'admins' => User::where('root_admin', true)->count(), + ], + ], + + 'nodes' => $nodes, + ]; + } +} diff --git a/app/Services/Users/ToggleTwoFactorService.php b/app/Services/Users/ToggleTwoFactorService.php index 6fe8bc9dd..a91615b1c 100644 --- a/app/Services/Users/ToggleTwoFactorService.php +++ b/app/Services/Users/ToggleTwoFactorService.php @@ -3,83 +3,87 @@ namespace Pterodactyl\Services\Users; use Carbon\Carbon; +use Illuminate\Support\Str; use Pterodactyl\Models\User; use PragmaRX\Google2FA\Google2FA; -use Illuminate\Contracts\Config\Repository; +use Illuminate\Database\ConnectionInterface; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; +use Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository; use Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid; class ToggleTwoFactorService { - /** - * @var \Illuminate\Contracts\Config\Repository - */ - private $config; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \PragmaRX\Google2FA\Google2FA - */ - private $google2FA; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $repository; - /** * ToggleTwoFactorService constructor. - * - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - * @param \PragmaRX\Google2FA\Google2FA $google2FA - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository */ public function __construct( - Encrypter $encrypter, - Google2FA $google2FA, - Repository $config, - UserRepositoryInterface $repository + private ConnectionInterface $connection, + private Encrypter $encrypter, + private Google2FA $google2FA, + private RecoveryTokenRepository $recoveryTokenRepository, + private UserRepositoryInterface $repository ) { - $this->config = $config; - $this->encrypter = $encrypter; - $this->google2FA = $google2FA; - $this->repository = $repository; } /** * Toggle 2FA on an account only if the token provided is valid. * - * @param \Pterodactyl\Models\User $user - * @param string $token - * @param bool|null $toggleState - * @return bool - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable + * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException + * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException + * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException * @throws \Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid */ - public function handle(User $user, string $token, bool $toggleState = null): bool + public function handle(User $user, string $token, bool $toggleState = null): array { - $window = $this->config->get('pterodactyl.auth.2fa.window'); $secret = $this->encrypter->decrypt($user->totp_secret); - $isValidToken = $this->google2FA->verifyKey($secret, $token, $window); + $isValidToken = $this->google2FA->verifyKey($secret, $token, config()->get('pterodactyl.auth.2fa.window')); - if (! $isValidToken) { - throw new TwoFactorAuthenticationTokenInvalid; + if (!$isValidToken) { + throw new TwoFactorAuthenticationTokenInvalid(); } - $this->repository->withoutFreshModel()->update($user->id, [ - 'totp_authenticated_at' => Carbon::now(), - 'use_totp' => (is_null($toggleState) ? ! $user->use_totp : $toggleState), - ]); + return $this->connection->transaction(function () use ($user, $toggleState) { + // Now that we're enabling 2FA on the account, generate 10 recovery tokens for the account + // and store them hashed in the database. We'll return them to the caller so that the user + // can see and save them. + // + // If a user is unable to login with a 2FA token they can provide one of these backup codes + // which will then be marked as deleted from the database and will also bypass 2FA protections + // on their account. + $tokens = []; + if ((!$toggleState && !$user->use_totp) || $toggleState) { + $inserts = []; + for ($i = 0; $i < 10; ++$i) { + $token = Str::random(10); - return true; + $inserts[] = [ + 'user_id' => $user->id, + 'token' => password_hash($token, PASSWORD_DEFAULT), + // insert() won't actually set the time on the models, so make sure we do this + // manually here. + 'created_at' => Carbon::now(), + ]; + + $tokens[] = $token; + } + + // Before inserting any new records make sure all of the old ones are deleted to avoid + // any issues or storing an unnecessary number of tokens in the database. + $this->recoveryTokenRepository->deleteWhere(['user_id' => $user->id]); + + // Bulk insert the hashed tokens. + $this->recoveryTokenRepository->insert($inserts); + } + + $this->repository->withoutFreshModel()->update($user->id, [ + 'totp_authenticated_at' => Carbon::now(), + 'use_totp' => (is_null($toggleState) ? !$user->use_totp : $toggleState), + ]); + + return $tokens; + }); } } diff --git a/app/Services/Users/TwoFactorSetupService.php b/app/Services/Users/TwoFactorSetupService.php index 4f2389b18..9ce832215 100644 --- a/app/Services/Users/TwoFactorSetupService.php +++ b/app/Services/Users/TwoFactorSetupService.php @@ -2,8 +2,6 @@ namespace Pterodactyl\Services\Users; -use Exception; -use RuntimeException; use Pterodactyl\Models\User; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; @@ -11,38 +9,16 @@ use Illuminate\Contracts\Config\Repository as ConfigRepository; class TwoFactorSetupService { - const VALID_BASE32_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - private $config; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - private $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $repository; + public const VALID_BASE32_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; /** * TwoFactorSetupService constructor. - * - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository */ public function __construct( - ConfigRepository $config, - Encrypter $encrypter, - UserRepositoryInterface $repository + private ConfigRepository $config, + private Encrypter $encrypter, + private UserRepositoryInterface $repository ) { - $this->config = $config; - $this->encrypter = $encrypter; - $this->repository = $repository; } /** @@ -50,34 +26,34 @@ class TwoFactorSetupService * QR code URL. This URL will need to be attached to a QR generating service in * order to function. * - * @param \Pterodactyl\Models\User $user - * @return string - * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function handle(User $user): string + public function handle(User $user): array { $secret = ''; try { - for ($i = 0; $i < $this->config->get('pterodactyl.auth.2fa.bytes', 16); $i++) { + for ($i = 0; $i < $this->config->get('pterodactyl.auth.2fa.bytes', 16); ++$i) { $secret .= substr(self::VALID_BASE32_CHARACTERS, random_int(0, 31), 1); } - } catch (Exception $exception) { - throw new RuntimeException($exception->getMessage(), 0, $exception); + } catch (\Exception $exception) { + throw new \RuntimeException($exception->getMessage(), 0, $exception); } $this->repository->withoutFreshModel()->update($user->id, [ 'totp_secret' => $this->encrypter->encrypt($secret), ]); - $company = preg_replace('/\s/', '', $this->config->get('app.name')); + $company = urlencode(preg_replace('/\s/', '', $this->config->get('app.name'))); - return sprintf( - 'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s', - rawurlencode($company), - rawurlencode($user->email), - rawurlencode($secret) - ); + return [ + 'image_url_data' => sprintf( + 'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s', + rawurlencode($company), + rawurlencode($user->email), + rawurlencode($secret), + ), + 'secret' => $secret, + ]; } } diff --git a/app/Services/Users/UserCreationService.php b/app/Services/Users/UserCreationService.php index b54c4f2de..130ae1a4e 100644 --- a/app/Services/Users/UserCreationService.php +++ b/app/Services/Users/UserCreationService.php @@ -3,6 +3,7 @@ namespace Pterodactyl\Services\Users; use Ramsey\Uuid\Uuid; +use Pterodactyl\Models\User; use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Database\ConnectionInterface; use Illuminate\Contracts\Auth\PasswordBroker; @@ -12,62 +13,30 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface; class UserCreationService { /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Illuminate\Contracts\Hashing\Hasher - */ - private $hasher; - - /** - * @var \Illuminate\Contracts\Auth\PasswordBroker - */ - private $passwordBroker; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $repository; - - /** - * CreationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Illuminate\Contracts\Hashing\Hasher $hasher - * @param \Illuminate\Contracts\Auth\PasswordBroker $passwordBroker - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository + * UserCreationService constructor. */ public function __construct( - ConnectionInterface $connection, - Hasher $hasher, - PasswordBroker $passwordBroker, - UserRepositoryInterface $repository + private ConnectionInterface $connection, + private Hasher $hasher, + private PasswordBroker $passwordBroker, + private UserRepositoryInterface $repository ) { - $this->connection = $connection; - $this->hasher = $hasher; - $this->passwordBroker = $passwordBroker; - $this->repository = $repository; } /** * Create a new user on the system. * - * @param array $data - * @return \Pterodactyl\Models\User - * * @throws \Exception * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function handle(array $data) + public function handle(array $data): User { - if (array_key_exists('password', $data) && ! empty($data['password'])) { + if (array_key_exists('password', $data) && !empty($data['password'])) { $data['password'] = $this->hasher->make($data['password']); } $this->connection->beginTransaction(); - if (! isset($data['password']) || empty($data['password'])) { + if (!isset($data['password']) || empty($data['password'])) { $generateResetToken = true; $data['password'] = $this->hasher->make(str_random(30)); } diff --git a/app/Services/Users/UserDeletionService.php b/app/Services/Users/UserDeletionService.php index 942e76faf..0f373240f 100644 --- a/app/Services/Users/UserDeletionService.php +++ b/app/Services/Users/UserDeletionService.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Users; @@ -18,46 +11,21 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class UserDeletionService { /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - protected $repository; - - /** - * @var \Illuminate\Contracts\Translation\Translator - */ - protected $translator; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * DeletionService constructor. - * - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Illuminate\Contracts\Translation\Translator $translator - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository + * UserDeletionService constructor. */ public function __construct( - ServerRepositoryInterface $serverRepository, - Translator $translator, - UserRepositoryInterface $repository + protected UserRepositoryInterface $repository, + protected ServerRepositoryInterface $serverRepository, + protected Translator $translator ) { - $this->repository = $repository; - $this->translator = $translator; - $this->serverRepository = $serverRepository; } /** * Delete a user from the panel only if they have no servers attached to their account. * - * @param int|\Pterodactyl\Models\User $user - * @return bool|null - * * @throws \Pterodactyl\Exceptions\DisplayException */ - public function handle($user) + public function handle(int|User $user): void { if ($user instanceof User) { $user = $user->id; @@ -65,9 +33,9 @@ class UserDeletionService $servers = $this->serverRepository->setColumns('id')->findCountWhere([['owner_id', '=', $user]]); if ($servers > 0) { - throw new DisplayException($this->translator->trans('admin/user.exceptions.user_has_servers')); + throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers')); } - return $this->repository->delete($user); + $this->repository->delete($user); } } diff --git a/app/Services/Users/UserUpdateService.php b/app/Services/Users/UserUpdateService.php index 440f8b45a..770fbdf0b 100644 --- a/app/Services/Users/UserUpdateService.php +++ b/app/Services/Users/UserUpdateService.php @@ -3,78 +3,35 @@ namespace Pterodactyl\Services\Users; use Pterodactyl\Models\User; -use Illuminate\Support\Collection; use Illuminate\Contracts\Hashing\Hasher; use Pterodactyl\Traits\Services\HasUserLevels; -use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Pterodactyl\Services\DaemonKeys\RevokeMultipleDaemonKeysService; class UserUpdateService { use HasUserLevels; /** - * @var \Illuminate\Contracts\Hashing\Hasher + * UserUpdateService constructor. */ - private $hasher; - - /** - * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface - */ - private $repository; - - /** - * @var \Pterodactyl\Services\DaemonKeys\RevokeMultipleDaemonKeysService - */ - private $revocationService; - - /** - * UpdateService constructor. - * - * @param \Illuminate\Contracts\Hashing\Hasher $hasher - * @param \Pterodactyl\Services\DaemonKeys\RevokeMultipleDaemonKeysService $revocationService - * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository - */ - public function __construct( - Hasher $hasher, - RevokeMultipleDaemonKeysService $revocationService, - UserRepositoryInterface $repository - ) { - $this->hasher = $hasher; - $this->repository = $repository; - $this->revocationService = $revocationService; + public function __construct(private Hasher $hasher) + { } /** - * Update the user model instance. If the user has been removed as an administrator - * revoke all of the authentication tokens that have been assigned to their account. + * Update the user model instance and return the updated model. * - * @param \Pterodactyl\Models\User $user - * @param array $data - * @return \Illuminate\Support\Collection - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Throwable */ - public function handle(User $user, array $data): Collection + public function handle(User $user, array $data): User { - if (! empty(array_get($data, 'password'))) { + if (!empty(array_get($data, 'password'))) { $data['password'] = $this->hasher->make($data['password']); } else { unset($data['password']); } - if ($this->isUserLevel(User::USER_LEVEL_ADMIN)) { - if (array_get($data, 'root_admin', 0) == 0 && $user->root_admin) { - $this->revocationService->handle($user, array_get($data, 'ignore_connection_error', false)); - } - } else { - unset($data['root_admin']); - } + $user->forceFill($data)->saveOrFail(); - return collect([ - 'model' => $this->repository->update($user->id, $data), - 'exceptions' => $this->revocationService->getExceptions(), - ]); + return $user->refresh(); } } diff --git a/app/Traits/Commands/EnvironmentWriterTrait.php b/app/Traits/Commands/EnvironmentWriterTrait.php index bc4d2486e..3d1aa0899 100644 --- a/app/Traits/Commands/EnvironmentWriterTrait.php +++ b/app/Traits/Commands/EnvironmentWriterTrait.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Traits\Commands; @@ -13,28 +6,36 @@ use Pterodactyl\Exceptions\PterodactylException; trait EnvironmentWriterTrait { + /** + * Escapes an environment value by looking for any characters that could + * reasonably cause environment parsing issues. Those values are then wrapped + * in quotes before being returned. + */ + public function escapeEnvironmentValue(string $value): string + { + if (!preg_match('/^\"(.*)\"$/', $value) && preg_match('/([^\w.\-+\/])+/', $value)) { + return sprintf('"%s"', addslashes($value)); + } + + return $value; + } + /** * Update the .env file for the application using the passed in values. * - * @param array $values - * * @throws \Pterodactyl\Exceptions\PterodactylException */ - public function writeToEnvironment(array $values = []) + public function writeToEnvironment(array $values = []): void { $path = base_path('.env'); - if (! file_exists($path)) { + if (!file_exists($path)) { throw new PterodactylException('Cannot locate .env file, was this software installed correctly?'); } $saveContents = file_get_contents($path); collect($values)->each(function ($value, $key) use (&$saveContents) { $key = strtoupper($key); - if (str_contains($value, ' ') && ! preg_match('/\"(.*)\"/', $value)) { - $value = sprintf('"%s"', addslashes($value)); - } - - $saveValue = sprintf('%s=%s', $key, $value); + $saveValue = sprintf('%s=%s', $key, $this->escapeEnvironmentValue($value)); if (preg_match_all('/^' . $key . '=(.*)$/m', $saveContents) < 1) { $saveContents = $saveContents . PHP_EOL . $saveValue; diff --git a/app/Traits/Controllers/JavascriptInjection.php b/app/Traits/Controllers/JavascriptInjection.php index bbda917d1..44fec983a 100644 --- a/app/Traits/Controllers/JavascriptInjection.php +++ b/app/Traits/Controllers/JavascriptInjection.php @@ -1,31 +1,17 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Traits\Controllers; -use Javascript; use Illuminate\Http\Request; trait JavascriptInjection { - /** - * @var \Illuminate\Http\Request - */ - private $request; + private Request $request; /** * Set the request object to use when injecting JS. - * - * @param \Illuminate\Http\Request $request - * @return $this */ - public function setRequest(Request $request) + public function setRequest(Request $request): self { $this->request = $request; @@ -33,32 +19,10 @@ trait JavascriptInjection } /** - * Injects server javascript into the page to be used by other services. - * - * @param array $args - * @param bool $overwrite - * @return array + * Injects the exact array passed in, nothing more. */ - public function injectJavascript($args = [], $overwrite = false) + public function plainInject(array $args = []): string { - $request = $this->request ?? app()->make(Request::class); - $server = $request->attributes->get('server'); - $token = $request->attributes->get('server_token'); - - $response = array_merge_recursive([ - 'server' => [ - 'uuid' => $server->uuid, - 'uuidShort' => $server->uuidShort, - 'daemonSecret' => $token, - ], - 'server_token' => $token, - 'node' => [ - 'fqdn' => $server->node->fqdn, - 'scheme' => $server->node->scheme, - 'daemonListen' => $server->node->daemonListen, - ], - ], $args); - - return Javascript::put($overwrite ? $args : $response); + return \JavaScript::put($args); } } diff --git a/app/Traits/Controllers/PlainJavascriptInjection.php b/app/Traits/Controllers/PlainJavascriptInjection.php index f514eb1af..2c0832cde 100644 --- a/app/Traits/Controllers/PlainJavascriptInjection.php +++ b/app/Traits/Controllers/PlainJavascriptInjection.php @@ -1,10 +1,4 @@ getFilesystemInstance()->directories(resource_path('lang')))->mapWithKeys(function ($path) use ($localize) { $code = basename($path); @@ -36,8 +27,6 @@ trait AvailableLanguages /** * Return an instance of the filesystem for getting a folder listing. - * - * @return \Illuminate\Filesystem\Filesystem */ private function getFilesystemInstance(): Filesystem { @@ -46,8 +35,6 @@ trait AvailableLanguages /** * Return an instance of the ISO639 class for generating names. - * - * @return \Matriphe\ISO639\ISO639 */ private function getIsoInstance(): ISO639 { diff --git a/app/Traits/Services/HasUserLevels.php b/app/Traits/Services/HasUserLevels.php index 29e49e8e6..33be0fbf1 100644 --- a/app/Traits/Services/HasUserLevels.php +++ b/app/Traits/Services/HasUserLevels.php @@ -6,18 +6,12 @@ use Pterodactyl\Models\User; trait HasUserLevels { - /** - * @var int - */ - private $userLevel = User::USER_LEVEL_USER; + private int $userLevel = User::USER_LEVEL_USER; /** * Set the access level for running this function. - * - * @param int $level - * @return $this */ - public function setUserLevel(int $level) + public function setUserLevel(int $level): self { $this->userLevel = $level; @@ -26,8 +20,6 @@ trait HasUserLevels /** * Determine which level this function is running at. - * - * @return int */ public function getUserLevel(): int { @@ -36,9 +28,6 @@ trait HasUserLevels /** * Determine if the current user level is set to a specific level. - * - * @param int $level - * @return bool */ public function isUserLevel(int $level): bool { diff --git a/app/Traits/Services/ReturnsUpdatedModels.php b/app/Traits/Services/ReturnsUpdatedModels.php index 2d5ee64fd..055e8971e 100644 --- a/app/Traits/Services/ReturnsUpdatedModels.php +++ b/app/Traits/Services/ReturnsUpdatedModels.php @@ -4,15 +4,9 @@ namespace Pterodactyl\Traits\Services; trait ReturnsUpdatedModels { - /** - * @var bool - */ - private $updatedModel = false; + private bool $updatedModel = false; - /** - * @return bool - */ - public function getUpdatedModel() + public function getUpdatedModel(): bool { return $this->updatedModel; } @@ -21,12 +15,8 @@ trait ReturnsUpdatedModels * If called a fresh model will be returned from the database. This is used * for API calls, but is unnecessary for UI based updates where the page is * being reloaded and a fresh model will be pulled anyways. - * - * @param bool $toggle - * - * @return $this */ - public function returnUpdatedModel(bool $toggle = true) + public function returnUpdatedModel(bool $toggle = true): self { $this->updatedModel = $toggle; diff --git a/app/Traits/Services/ValidatesValidationRules.php b/app/Traits/Services/ValidatesValidationRules.php index d2010c828..5456cb017 100644 --- a/app/Traits/Services/ValidatesValidationRules.php +++ b/app/Traits/Services/ValidatesValidationRules.php @@ -2,36 +2,28 @@ namespace Pterodactyl\Traits\Services; -use BadMethodCallException; use Illuminate\Support\Str; -use Illuminate\Contracts\Validation\Factory; +use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException; trait ValidatesValidationRules { - /** - * @return \Illuminate\Contracts\Validation\Factory - */ - abstract protected function getValidator(): Factory; + abstract protected function getValidator(): ValidationFactory; /** * Validate that the rules being provided are valid for Laravel and can * be resolved. * - * @param array|string $rules - * * @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException */ - public function validateRules($rules) + public function validateRules(array|string $rules): void { try { $this->getValidator()->make(['__TEST' => 'test'], ['__TEST' => $rules])->fails(); - } catch (BadMethodCallException $exception) { + } catch (\BadMethodCallException $exception) { $matches = []; if (preg_match('/Method \[(.+)\] does not exist\./', $exception->getMessage(), $matches)) { - throw new BadValidationRuleException(trans('exceptions.nest.variables.bad_validation_rule', [ - 'rule' => Str::snake(str_replace('validate', '', array_get($matches, 1, 'unknownRule'))), - ]), $exception); + throw new BadValidationRuleException(trans('exceptions.nest.variables.bad_validation_rule', ['rule' => Str::snake(str_replace('validate', '', array_get($matches, 1, 'unknownRule')))]), $exception); } throw $exception; diff --git a/app/Transformers/Api/Application/AdminRoleTransformer.php b/app/Transformers/Api/Application/AdminRoleTransformer.php new file mode 100644 index 000000000..7a561528a --- /dev/null +++ b/app/Transformers/Api/Application/AdminRoleTransformer.php @@ -0,0 +1,29 @@ + $model->id, + 'name' => $model->name, + 'description' => $model->description, + ]; + } +} diff --git a/app/Transformers/Api/Application/AllocationTransformer.php b/app/Transformers/Api/Application/AllocationTransformer.php index f1a35f4f2..4aafc99f8 100644 --- a/app/Transformers/Api/Application/AllocationTransformer.php +++ b/app/Transformers/Api/Application/AllocationTransformer.php @@ -2,22 +2,18 @@ namespace Pterodactyl\Transformers\Api\Application; +use League\Fractal\Resource\Item; use Pterodactyl\Models\Allocation; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class AllocationTransformer extends BaseTransformer +class AllocationTransformer extends Transformer { - /** - * Relationships that can be loaded onto allocation transformations. - * - * @var array - */ - protected $availableIncludes = ['node', 'server']; + protected array $availableIncludes = ['node', 'server']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -26,58 +22,41 @@ class AllocationTransformer extends BaseTransformer /** * Return a generic transformed allocation array. - * - * @param \Pterodactyl\Models\Allocation $allocation - * @return array */ - public function transform(Allocation $allocation) + public function transform(Allocation $model): array { return [ - 'id' => $allocation->id, - 'ip' => $allocation->ip, - 'alias' => $allocation->ip_alias, - 'port' => $allocation->port, - 'assigned' => ! is_null($allocation->server_id), + 'id' => $model->id, + 'ip' => $model->ip, + 'alias' => $model->ip_alias, + 'port' => $model->port, + 'notes' => $model->notes, + 'server_id' => $model->server_id, + 'assigned' => !is_null($model->server_id), ]; } /** * Load the node relationship onto a given transformation. - * - * @param \Pterodactyl\Models\Allocation $allocation - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeNode(Allocation $allocation) + public function includeNode(Allocation $allocation): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { return $this->null(); } - $allocation->loadMissing('node'); - - return $this->item( - $allocation->getRelation('node'), $this->makeTransformer(NodeTransformer::class), 'node' - ); + return $this->item($allocation->node, new NodeTransformer()); } /** * Load the server relationship onto a given transformation. - * - * @param \Pterodactyl\Models\Allocation $allocation - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeServer(Allocation $allocation) + public function includeServer(Allocation $allocation): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS) || !$allocation->server) { return $this->null(); } - $allocation->loadMissing('server'); - - return $this->item( - $allocation->getRelation('server'), $this->makeTransformer(ServerTransformer::class), 'server' - ); + return $this->item($allocation->server, new ServerTransformer()); } } diff --git a/app/Transformers/Api/Application/BaseTransformer.php b/app/Transformers/Api/Application/BaseTransformer.php deleted file mode 100644 index be5e367f6..000000000 --- a/app/Transformers/Api/Application/BaseTransformer.php +++ /dev/null @@ -1,115 +0,0 @@ -call([$this, 'handle']); - } - } - - /** - * Set the HTTP request class being used for this request. - * - * @param \Pterodactyl\Models\ApiKey $key - * @return $this - */ - public function setKey(ApiKey $key) - { - $this->key = $key; - - return $this; - } - - /** - * Return the request instance being used for this transformer. - * - * @return \Pterodactyl\Models\ApiKey - */ - public function getKey(): ApiKey - { - return $this->key; - } - - /** - * Determine if the API key loaded onto the transformer has permission - * to access a different resource. This is used when including other - * models on a transformation request. - * - * @param string $resource - * @return bool - */ - protected function authorize(string $resource): bool - { - return AdminAcl::check($this->getKey(), $resource, AdminAcl::READ); - } - - /** - * Create a new instance of the transformer and pass along the currently - * set API key. - * - * @param string $abstract - * @param array $parameters - * @return \Pterodactyl\Transformers\Api\Application\BaseTransformer - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - protected function makeTransformer(string $abstract, array $parameters = []) - { - /** @var \Pterodactyl\Transformers\Api\Application\BaseTransformer $transformer */ - $transformer = Container::getInstance()->makeWith($abstract, $parameters); - $transformer->setKey($this->getKey()); - - if (! $transformer instanceof self || $transformer instanceof BaseClientTransformer) { - throw new InvalidTransformerLevelException('Calls to ' . __METHOD__ . ' must return a transformer that is an instance of ' . __CLASS__); - } - - return $transformer; - } - - /** - * Return an ISO-8601 formatted timestamp to use in the API response. - * - * @param string $timestamp - * @return string - */ - protected function formatTimestamp(string $timestamp): string - { - return Chronos::createFromFormat(Chronos::DEFAULT_TO_STRING_FORMAT, $timestamp) - ->setTimezone(self::RESPONSE_TIMEZONE) - ->toIso8601String(); - } -} diff --git a/app/Transformers/Api/Application/DatabaseHostTransformer.php b/app/Transformers/Api/Application/DatabaseHostTransformer.php index 3ef10e8ad..dfe18c7c4 100644 --- a/app/Transformers/Api/Application/DatabaseHostTransformer.php +++ b/app/Transformers/Api/Application/DatabaseHostTransformer.php @@ -2,24 +2,18 @@ namespace Pterodactyl\Transformers\Api\Application; -use Cake\Chronos\Chronos; -use Pterodactyl\Models\Database; use Pterodactyl\Models\DatabaseHost; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class DatabaseHostTransformer extends BaseTransformer +class DatabaseHostTransformer extends Transformer { - /** - * @var array - */ - protected $availableIncludes = [ - 'databases', - ]; + protected array $availableIncludes = ['databases']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -28,11 +22,8 @@ class DatabaseHostTransformer extends BaseTransformer /** * Transform database host into a representation for the application API. - * - * @param \Pterodactyl\Models\DatabaseHost $model - * @return array */ - public function transform(DatabaseHost $model) + public function transform(DatabaseHost $model): array { return [ 'id' => $model->id, @@ -40,31 +31,21 @@ class DatabaseHostTransformer extends BaseTransformer 'host' => $model->host, 'port' => $model->port, 'username' => $model->username, - 'node' => $model->node_id, - 'created_at' => Chronos::createFromFormat(Chronos::DEFAULT_TO_STRING_FORMAT, $model->created_at) - ->setTimezone(config('app.timezone')) - ->toIso8601String(), - 'updated_at' => Chronos::createFromFormat(Chronos::DEFAULT_TO_STRING_FORMAT, $model->updated_at) - ->setTimezone(config('app.timezone')) - ->toIso8601String(), + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } /** * Include the databases associated with this host. - * - * @param \Pterodactyl\Models\DatabaseHost $model - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeDatabases(DatabaseHost $model) + public function includeDatabases(DatabaseHost $model): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { return $this->null(); } - $model->loadMissing('databases'); - - return $this->collection($model->getRelation('databases'), $this->makeTransformer(ServerDatabaseTransformer::class), Database::RESOURCE_NAME); + // TODO + return $this->collection($model->databases, new ServerDatabaseTransformer()); } } diff --git a/app/Transformers/Api/Application/EggTransformer.php b/app/Transformers/Api/Application/EggTransformer.php index a3686341e..ab7328f94 100644 --- a/app/Transformers/Api/Application/EggTransformer.php +++ b/app/Transformers/Api/Application/EggTransformer.php @@ -3,26 +3,27 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Egg; -use Pterodactyl\Models\Nest; -use Pterodactyl\Models\Server; -use Pterodactyl\Models\EggVariable; +use League\Fractal\Resource\Item; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class EggTransformer extends BaseTransformer +class EggTransformer extends Transformer { /** * Relationships that can be loaded onto this transformation. - * - * @var array */ - protected $availableIncludes = [ - 'nest', 'servers', 'config', 'script', 'variables', + protected array $availableIncludes = [ + 'config', + 'nest', + 'script', + 'servers', + 'variables', ]; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -33,24 +34,28 @@ class EggTransformer extends BaseTransformer * Transform an Egg model into a representation that can be consumed by * the application api. * - * @param \Pterodactyl\Models\Egg $model - * @return array + * @throws \JsonException */ - public function transform(Egg $model) + public function transform(Egg $model): array { + $files = json_decode($model->config_files, true, 512, JSON_THROW_ON_ERROR); + if (empty($files)) { + $files = new \stdClass(); + } + return [ 'id' => $model->id, 'uuid' => $model->uuid, 'name' => $model->name, - 'nest' => $model->nest_id, + 'nest_id' => $model->nest_id, 'author' => $model->author, 'description' => $model->description, - 'docker_image' => $model->docker_image, + 'docker_images' => $model->docker_images, 'config' => [ - 'files' => json_decode($model->config_files, true), + 'files' => $files, 'startup' => json_decode($model->config_startup, true), 'stop' => $model->config_stop, - 'logs' => json_decode($model->config_logs, true), + 'file_denylist' => $model->file_denylist, 'extends' => $model->config_from, ], 'startup' => $model->startup, @@ -61,87 +66,52 @@ class EggTransformer extends BaseTransformer 'container' => $model->script_container, 'extends' => $model->copy_script_from, ], - $model->getCreatedAtColumn() => $this->formatTimestamp($model->created_at), - $model->getUpdatedAtColumn() => $this->formatTimestamp($model->updated_at), + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } - /** - * Include the Nest relationship for the given Egg in the transformation. - * - * @param \Pterodactyl\Models\Egg $model - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - public function includeNest(Egg $model) - { - if (! $this->authorize(AdminAcl::RESOURCE_NESTS)) { - return $this->null(); - } - - $model->loadMissing('nest'); - - return $this->item($model->getRelation('nest'), $this->makeTransformer(NestTransformer::class), Nest::RESOURCE_NAME); - } - - /** - * Include the Servers relationship for the given Egg in the transformation. - * - * @param \Pterodactyl\Models\Egg $model - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - public function includeServers(Egg $model) - { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { - return $this->null(); - } - - $model->loadMissing('servers'); - - return $this->collection($model->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), Server::RESOURCE_NAME); - } - /** * Include more detailed information about the configuration if this Egg is * extending another. - * - * @param \Pterodactyl\Models\Egg $model - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource */ - public function includeConfig(Egg $model) + public function includeConfig(Egg $model): Item|NullResource { if (is_null($model->config_from)) { return $this->null(); } - $model->loadMissing('configFrom'); - return $this->item($model, function (Egg $model) { return [ 'files' => json_decode($model->inherit_config_files), 'startup' => json_decode($model->inherit_config_startup), 'stop' => $model->inherit_config_stop, - 'logs' => json_decode($model->inherit_config_logs), ]; }); } + /** + * Include the Nest relationship for the given Egg in the transformation. + */ + public function includeNest(Egg $model): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NESTS)) { + return $this->null(); + } + + return $this->item($model->nest, new NestTransformer()); + } + /** * Include more detailed information about the script configuration if the * Egg is extending another. - * - * @param \Pterodactyl\Models\Egg $model - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource */ - public function includeScript(Egg $model) + public function includeScript(Egg $model): Item|NullResource { if (is_null($model->copy_script_from)) { return $this->null(); } - $model->loadMissing('scriptFrom'); - return $this->item($model, function (Egg $model) { return [ 'privileged' => $model->script_is_privileged, @@ -153,24 +123,28 @@ class EggTransformer extends BaseTransformer } /** - * Include the variables that are defined for this Egg. - * - * @param \Pterodactyl\Models\Egg $model - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Include the Servers relationship for the given Egg in the transformation. */ - public function includeVariables(Egg $model) + public function includeServers(Egg $model): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + return $this->collection($model->servers, new ServerTransformer()); + } + + /** + * Include the variables that are defined for this Egg. + */ + public function includeVariables(Egg $model): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { return $this->null(); } $model->loadMissing('variables'); - return $this->collection( - $model->getRelation('variables'), - $this->makeTransformer(EggVariableTransformer::class), - EggVariable::RESOURCE_NAME - ); + return $this->collection($model->variables, new EggVariableTransformer()); } } diff --git a/app/Transformers/Api/Application/EggVariableTransformer.php b/app/Transformers/Api/Application/EggVariableTransformer.php index decb038ab..613b5768d 100644 --- a/app/Transformers/Api/Application/EggVariableTransformer.php +++ b/app/Transformers/Api/Application/EggVariableTransformer.php @@ -4,20 +4,22 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Egg; use Pterodactyl\Models\EggVariable; +use Pterodactyl\Transformers\Api\Transformer; -class EggVariableTransformer extends BaseTransformer +class EggVariableTransformer extends Transformer { /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { return Egg::RESOURCE_NAME; } - public function transform(EggVariable $model) + /** + * Transform egg variable into a representation for the application API. + */ + public function transform(EggVariable $model): array { return $model->toArray(); } diff --git a/app/Transformers/Api/Application/LocationTransformer.php b/app/Transformers/Api/Application/LocationTransformer.php index d54e77d20..5ef3e74d5 100644 --- a/app/Transformers/Api/Application/LocationTransformer.php +++ b/app/Transformers/Api/Application/LocationTransformer.php @@ -3,21 +3,20 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Location; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class LocationTransformer extends BaseTransformer +class LocationTransformer extends Transformer { /** * List of resources that can be included. - * - * @var array */ - protected $availableIncludes = ['nodes', 'servers']; + protected array $availableIncludes = ['nodes', 'servers']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -25,57 +24,40 @@ class LocationTransformer extends BaseTransformer } /** - * Return a generic transformed pack array. - * - * @param \Pterodactyl\Models\Location $location - * @return array + * Return a generic transformed location array. */ - public function transform(Location $location): array + public function transform(Location $model): array { return [ - 'id' => $location->id, - 'short' => $location->short, - 'long' => $location->long, - $location->getUpdatedAtColumn() => $this->formatTimestamp($location->updated_at), - $location->getCreatedAtColumn() => $this->formatTimestamp($location->created_at), + 'id' => $model->id, + 'short' => $model->short, + 'long' => $model->long, + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } /** * Return the nodes associated with this location. - * - * @param \Pterodactyl\Models\Location $location - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeServers(Location $location) + public function includeNodes(Location $location): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { return $this->null(); } - $location->loadMissing('servers'); - - return $this->collection($location->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server'); + return $this->collection($location->nodes, new NodeTransformer()); } /** * Return the nodes associated with this location. - * - * @param \Pterodactyl\Models\Location $location - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeNodes(Location $location) + public function includeServers(Location $location): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { return $this->null(); } - $location->loadMissing('nodes'); - - return $this->collection($location->getRelation('nodes'), $this->makeTransformer(NodeTransformer::class), 'node'); + return $this->collection($location->servers, new ServerTransformer()); } } diff --git a/app/Transformers/Api/Application/MountTransformer.php b/app/Transformers/Api/Application/MountTransformer.php new file mode 100644 index 000000000..8c0d1f289 --- /dev/null +++ b/app/Transformers/Api/Application/MountTransformer.php @@ -0,0 +1,75 @@ + $model->id, + 'uuid' => $model->uuid, + 'name' => $model->name, + 'description' => $model->description, + 'source' => $model->source, + 'target' => $model->target, + 'read_only' => $model->read_only, + 'user_mountable' => $model->user_mountable, + ]; + } + + /** + * Return the eggs associated with this mount. + */ + public function includeEggs(Mount $mount): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + return $this->null(); + } + + return $this->collection($mount->eggs, new EggTransformer()); + } + + /** + * Return the nodes associated with this mount. + */ + public function includeNodes(Mount $mount): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + return $this->null(); + } + + return $this->collection($mount->nodes, new NodeTransformer()); + } + + /** + * Return the servers associated with this mount. + */ + public function includeServers(Mount $mount): Collection|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); + } + + return $this->collection($mount->servers, new ServerTransformer()); + } +} diff --git a/app/Transformers/Api/Application/NestTransformer.php b/app/Transformers/Api/Application/NestTransformer.php index 80154a682..8521564c6 100644 --- a/app/Transformers/Api/Application/NestTransformer.php +++ b/app/Transformers/Api/Application/NestTransformer.php @@ -2,26 +2,21 @@ namespace Pterodactyl\Transformers\Api\Application; -use Pterodactyl\Models\Egg; use Pterodactyl\Models\Nest; -use Pterodactyl\Models\Server; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class NestTransformer extends BaseTransformer +class NestTransformer extends Transformer { /** * Relationships that can be loaded onto this transformation. - * - * @var array */ - protected $availableIncludes = [ - 'eggs', 'servers', - ]; + protected array $availableIncludes = ['eggs', 'servers']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -31,55 +26,38 @@ class NestTransformer extends BaseTransformer /** * Transform a Nest model into a representation that can be consumed by the * application API. - * - * @param \Pterodactyl\Models\Nest $model - * @return array */ - public function transform(Nest $model) + public function transform(Nest $model): array { $response = $model->toArray(); - $response[$model->getUpdatedAtColumn()] = $this->formatTimestamp($model->updated_at); - $response[$model->getCreatedAtColumn()] = $this->formatTimestamp($model->created_at); + $response['created_at'] = self::formatTimestamp($model->created_at); + $response['updated_at'] = self::formatTimestamp($model->updated_at); return $response; } /** * Include the Eggs relationship on the given Nest model transformation. - * - * @param \Pterodactyl\Models\Nest $model - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeEggs(Nest $model) + public function includeEggs(Nest $model): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { return $this->null(); } - $model->loadMissing('eggs'); - - return $this->collection($model->getRelation('eggs'), $this->makeTransformer(EggTransformer::class), Egg::RESOURCE_NAME); + return $this->collection($model->eggs, new EggTransformer()); } /** * Include the servers relationship on the given Nest model. - * - * @param \Pterodactyl\Models\Nest $model - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeServers(Nest $model) + public function includeServers(Nest $model): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { return $this->null(); } - $model->loadMissing('servers'); - - return $this->collection($model->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), Server::RESOURCE_NAME); + return $this->collection($model->servers, new ServerTransformer()); } } diff --git a/app/Transformers/Api/Application/NodeTransformer.php b/app/Transformers/Api/Application/NodeTransformer.php index d9b8b61f3..093fcc962 100644 --- a/app/Transformers/Api/Application/NodeTransformer.php +++ b/app/Transformers/Api/Application/NodeTransformer.php @@ -3,21 +3,21 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Node; +use League\Fractal\Resource\Item; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class NodeTransformer extends BaseTransformer +class NodeTransformer extends Transformer { /** * List of resources that can be included. - * - * @var array */ - protected $availableIncludes = ['allocations', 'location', 'servers']; + protected array $availableIncludes = ['allocations', 'location', 'servers']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -27,83 +27,57 @@ class NodeTransformer extends BaseTransformer /** * Return a node transformed into a format that can be consumed by the * external administrative API. - * - * @param \Pterodactyl\Models\Node $node - * @return array */ - public function transform(Node $node): array + public function transform(Node $model): array { - $response = collect($node->toArray())->mapWithKeys(function ($value, $key) { - // I messed up early in 2016 when I named this column as poorly - // as I did. This is the tragic result of my mistakes. - $key = ($key === 'daemonSFTP') ? 'daemonSftp' : $key; + $response = $model->toArray(); - return [snake_case($key) => $value]; - })->toArray(); + $response['created_at'] = self::formatTimestamp($model->created_at); + $response['updated_at'] = self::formatTimestamp($model->updated_at); - $response[$node->getUpdatedAtColumn()] = $this->formatTimestamp($node->updated_at); - $response[$node->getCreatedAtColumn()] = $this->formatTimestamp($node->created_at); + $resources = $model->servers()->select(['memory', 'disk'])->get(); + + $response['allocated_resources'] = [ + 'memory' => $resources->sum('memory'), + 'disk' => $resources->sum('disk'), + ]; return $response; } /** - * Return the nodes associated with this location. - * - * @param \Pterodactyl\Models\Node $node - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return the allocations associated with this node. */ - public function includeAllocations(Node $node) + public function includeAllocations(Node $node): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { + if (!$this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { return $this->null(); } - $node->loadMissing('allocations'); - - return $this->collection( - $node->getRelation('allocations'), $this->makeTransformer(AllocationTransformer::class), 'allocation' - ); + return $this->collection($node->allocations, new AllocationTransformer()); } /** - * Return the nodes associated with this location. - * - * @param \Pterodactyl\Models\Node $node - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return the location associated with this node. */ - public function includeLocation(Node $node) + public function includeLocation(Node $node): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_LOCATIONS)) { + if (!$this->authorize(AdminAcl::RESOURCE_LOCATIONS)) { return $this->null(); } - $node->loadMissing('location'); - - return $this->item( - $node->getRelation('location'), $this->makeTransformer(LocationTransformer::class), 'location' - ); + return $this->item($node->location, new LocationTransformer()); } /** - * Return the nodes associated with this location. - * - * @param \Pterodactyl\Models\Node $node - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return the servers associated with this node. */ - public function includeServers(Node $node) + public function includeServers(Node $node): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { return $this->null(); } - $node->loadMissing('servers'); - - return $this->collection( - $node->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server' - ); + return $this->collection($node->servers, new ServerTransformer()); } } diff --git a/app/Transformers/Api/Application/PackTransformer.php b/app/Transformers/Api/Application/PackTransformer.php deleted file mode 100644 index e77bdd459..000000000 --- a/app/Transformers/Api/Application/PackTransformer.php +++ /dev/null @@ -1,40 +0,0 @@ - $pack->id, - 'uuid' => $pack->uuid, - 'egg' => $pack->egg_id, - 'name' => $pack->name, - 'description' => $pack->description, - 'is_selectable' => (bool) $pack->selectable, - 'is_visible' => (bool) $pack->visible, - 'is_locked' => (bool) $pack->locked, - 'created_at' => $this->formatTimestamp($pack->created_at), - 'updated_at' => $this->formatTimestamp($pack->updated_at), - ]; - } -} diff --git a/app/Transformers/Api/Application/ServerDatabaseTransformer.php b/app/Transformers/Api/Application/ServerDatabaseTransformer.php index 1cdced612..159ac6b2d 100644 --- a/app/Transformers/Api/Application/ServerDatabaseTransformer.php +++ b/app/Transformers/Api/Application/ServerDatabaseTransformer.php @@ -2,29 +2,21 @@ namespace Pterodactyl\Transformers\Api\Application; -use Cake\Chronos\Chronos; use Pterodactyl\Models\Database; use League\Fractal\Resource\Item; -use Pterodactyl\Models\DatabaseHost; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; use Illuminate\Contracts\Encryption\Encrypter; -class ServerDatabaseTransformer extends BaseTransformer +class ServerDatabaseTransformer extends Transformer { - /** - * @var array - */ - protected $availableIncludes = ['password', 'host']; + protected array $availableIncludes = ['host', 'password']; - /** - * @var Encrypter - */ - private $encrypter; + private Encrypter $encrypter; /** * Perform dependency injection. - * - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter */ public function handle(Encrypter $encrypter) { @@ -33,8 +25,6 @@ class ServerDatabaseTransformer extends BaseTransformer /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -43,33 +33,36 @@ class ServerDatabaseTransformer extends BaseTransformer /** * Transform a database model in a representation for the application API. - * - * @param \Pterodactyl\Models\Database $model - * @return array */ public function transform(Database $model): array { return [ 'id' => $model->id, - 'server' => $model->server_id, - 'host' => $model->database_host_id, - 'database' => $model->database, + 'database_host_id' => $model->database_host_id, + 'server_id' => $model->server_id, + 'name' => $model->database, 'username' => $model->username, 'remote' => $model->remote, - 'created_at' => Chronos::createFromFormat(Chronos::DEFAULT_TO_STRING_FORMAT, $model->created_at) - ->setTimezone(config('app.timezone')) - ->toIso8601String(), - 'updated_at' => Chronos::createFromFormat(Chronos::DEFAULT_TO_STRING_FORMAT, $model->updated_at) - ->setTimezone(config('app.timezone')) - ->toIso8601String(), + 'max_connections' => $model->max_connections, + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } + /** + * Return the database host relationship for this server database. + */ + public function includeHost(Database $model): Item|NullResource + { + if (!$this->authorize(AdminAcl::RESOURCE_DATABASE_HOSTS)) { + return $this->null(); + } + + return $this->item($model->host, new DatabaseHostTransformer()); + } + /** * Include the database password in the request. - * - * @param \Pterodactyl\Models\Database $model - * @return \League\Fractal\Resource\Item */ public function includePassword(Database $model): Item { @@ -79,26 +72,4 @@ class ServerDatabaseTransformer extends BaseTransformer ]; }, 'database_password'); } - - /** - * Return the database host relationship for this server database. - * - * @param \Pterodactyl\Models\Database $model - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - public function includeHost(Database $model) - { - if (! $this->authorize(AdminAcl::RESOURCE_DATABASE_HOSTS)) { - return $this->null(); - } - - $model->loadMissing('host'); - - return $this->item( - $model->getRelation('host'), - $this->makeTransformer(DatabaseHostTransformer::class), - DatabaseHost::RESOURCE_NAME - ); - } } diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php index 70dc185d6..f34b1232f 100644 --- a/app/Transformers/Api/Application/ServerTransformer.php +++ b/app/Transformers/Api/Application/ServerTransformer.php @@ -3,38 +3,35 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Server; +use League\Fractal\Resource\Item; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; use Pterodactyl\Services\Servers\EnvironmentService; -class ServerTransformer extends BaseTransformer +class ServerTransformer extends Transformer { - /** - * @var \Pterodactyl\Services\Servers\EnvironmentService - */ - private $environmentService; + private EnvironmentService $environmentService; /** * List of resources that can be included. - * - * @var array */ - protected $availableIncludes = [ + protected array $availableIncludes = [ 'allocations', 'user', 'subusers', - 'pack', 'nest', 'egg', 'variables', 'location', 'node', 'databases', + 'transfer', ]; /** * Perform dependency injection. - * - * @param \Pterodactyl\Services\Servers\EnvironmentService $environmentService */ public function handle(EnvironmentService $environmentService) { @@ -43,8 +40,6 @@ class ServerTransformer extends BaseTransformer /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -53,240 +48,151 @@ class ServerTransformer extends BaseTransformer /** * Return a generic transformed server array. - * - * @param \Pterodactyl\Models\Server $server - * @return array - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function transform(Server $server): array + public function transform(Server $model): array { return [ - 'id' => $server->getKey(), - 'external_id' => $server->external_id, - 'uuid' => $server->uuid, - 'identifier' => $server->uuidShort, - 'name' => $server->name, - 'description' => $server->description, - 'suspended' => (bool) $server->suspended, + 'id' => $model->getKey(), + 'external_id' => $model->external_id, + 'uuid' => $model->uuid, + 'identifier' => $model->uuidShort, + 'name' => $model->name, + 'description' => $model->description, + 'status' => $model->status, 'limits' => [ - 'memory' => $server->memory, - 'swap' => $server->swap, - 'disk' => $server->disk, - 'io' => $server->io, - 'cpu' => $server->cpu, + 'cpu' => $model->cpu, + 'disk' => $model->disk, + 'io' => $model->io, + 'memory' => $model->memory, + 'oom_killer' => $model->oom_killer, + 'swap' => $model->swap, + 'threads' => $model->threads, ], 'feature_limits' => [ - 'databases' => $server->database_limit, - 'allocations' => $server->allocation_limit, + 'allocations' => $model->allocation_limit, + 'backups' => $model->backup_limit, + 'databases' => $model->database_limit, ], - 'user' => $server->owner_id, - 'node' => $server->node_id, - 'allocation' => $server->allocation_id, - 'nest' => $server->nest_id, - 'egg' => $server->egg_id, - 'pack' => $server->pack_id, + 'owner_id' => $model->owner_id, + 'node_id' => $model->node_id, + 'allocation_id' => $model->allocation_id, + 'nest_id' => $model->nest_id, + 'egg_id' => $model->egg_id, 'container' => [ - 'startup_command' => $server->startup, - 'image' => $server->image, - 'installed' => (int) $server->installed === 1, - 'environment' => $this->environmentService->handle($server), + 'startup' => $model->startup, + 'image' => $model->image, + 'environment' => $this->environmentService->handle($model), ], - $server->getUpdatedAtColumn() => $this->formatTimestamp($server->updated_at), - $server->getCreatedAtColumn() => $this->formatTimestamp($server->created_at), + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } /** * Return a generic array of allocations for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeAllocations(Server $server) + public function includeAllocations(Server $server): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { + if (!$this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { return $this->null(); } - $server->loadMissing('allocations'); - - return $this->collection($server->getRelation('allocations'), $this->makeTransformer(AllocationTransformer::class), 'allocation'); + return $this->collection($server->allocations, new AllocationTransformer()); } /** * Return a generic array of data about subusers for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeSubusers(Server $server) + public function includeSubusers(Server $server): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_USERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { return $this->null(); } - $server->loadMissing('subusers'); - - return $this->collection($server->getRelation('subusers'), $this->makeTransformer(SubuserTransformer::class), 'subuser'); + return $this->collection($server->subusers, new SubuserTransformer()); } /** * Return a generic array of data about subusers for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeUser(Server $server) + public function includeUser(Server $server): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_USERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { return $this->null(); } - $server->loadMissing('user'); - - return $this->item($server->getRelation('user'), $this->makeTransformer(UserTransformer::class), 'user'); - } - - /** - * Return a generic array with pack information for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - public function includePack(Server $server) - { - if (! $this->authorize(AdminAcl::RESOURCE_PACKS)) { - return $this->null(); - } - - $server->loadMissing('pack'); - if (is_null($server->getRelation('pack'))) { - return $this->null(); - } - - return $this->item($server->getRelation('pack'), $this->makeTransformer(PackTransformer::class), 'pack'); + return $this->item($server->user, new UserTransformer()); } /** * Return a generic array with nest information for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeNest(Server $server) + public function includeNest(Server $server): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_NESTS)) { + if (!$this->authorize(AdminAcl::RESOURCE_NESTS)) { return $this->null(); } - $server->loadMissing('nest'); - - return $this->item($server->getRelation('nest'), $this->makeTransformer(NestTransformer::class), 'nest'); + return $this->item($server->nest, new NestTransformer()); } /** * Return a generic array with egg information for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeEgg(Server $server) + public function includeEgg(Server $server): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { return $this->null(); } - $server->loadMissing('egg'); - - return $this->item($server->getRelation('egg'), $this->makeTransformer(EggTransformer::class), 'egg'); + return $this->item($server->egg, new EggTransformer()); } /** * Return a generic array of data about subusers for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeVariables(Server $server) + public function includeVariables(Server $server): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { return $this->null(); } - $server->loadMissing('variables'); - - return $this->collection($server->getRelation('variables'), $this->makeTransformer(ServerVariableTransformer::class), 'server_variable'); + return $this->collection($server->variables, new ServerVariableTransformer()); } /** - * Return a generic array with pack information for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return a generic array with location information for this server. */ - public function includeLocation(Server $server) + public function includeLocation(Server $server): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_LOCATIONS)) { + if (!$this->authorize(AdminAcl::RESOURCE_LOCATIONS)) { return $this->null(); } - $server->loadMissing('location'); - - return $this->item($server->getRelation('location'), $this->makeTransformer(LocationTransformer::class), 'location'); + return $this->item($server->location, new LocationTransformer()); } /** - * Return a generic array with pack information for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return a generic array with node information for this server. */ - public function includeNode(Server $server) + public function includeNode(Server $server): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { return $this->null(); } - $server->loadMissing('node'); - - return $this->item($server->getRelation('node'), $this->makeTransformer(NodeTransformer::class), 'node'); + return $this->item($server->node, new NodeTransformer()); } /** * Return a generic array with database information for this server. - * - * @param \Pterodactyl\Models\Server $server - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeDatabases(Server $server) + public function includeDatabases(Server $server): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { return $this->null(); } - $server->loadMissing('databases'); - - return $this->collection($server->getRelation('databases'), $this->makeTransformer(ServerDatabaseTransformer::class), 'databases'); + return $this->collection($server->databases, new ServerDatabaseTransformer()); } } diff --git a/app/Transformers/Api/Application/ServerVariableTransformer.php b/app/Transformers/Api/Application/ServerVariableTransformer.php index 73cd169b2..eef631a65 100644 --- a/app/Transformers/Api/Application/ServerVariableTransformer.php +++ b/app/Transformers/Api/Application/ServerVariableTransformer.php @@ -2,22 +2,14 @@ namespace Pterodactyl\Transformers\Api\Application; +use Pterodactyl\Models\EggVariable; use Pterodactyl\Models\ServerVariable; -use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class ServerVariableTransformer extends BaseTransformer +class ServerVariableTransformer extends Transformer { - /** - * List of resources that can be included. - * - * @var array - */ - protected $availableIncludes = ['parent']; - /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -26,30 +18,9 @@ class ServerVariableTransformer extends BaseTransformer /** * Return a generic transformed server variable array. - * - * @param \Pterodactyl\Models\ServerVariable $variable - * @return array */ - public function transform(ServerVariable $variable) + public function transform(EggVariable $model): array { - return $variable->toArray(); - } - - /** - * Return the parent service variable data. - * - * @param \Pterodactyl\Models\ServerVariable $variable - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - public function includeParent(ServerVariable $variable) - { - if (! $this->authorize(AdminAcl::RESOURCE_EGGS)) { - return $this->null(); - } - - $variable->loadMissing('variable'); - - return $this->item($variable->getRelation('variable'), $this->makeTransformer(EggVariableTransformer::class), 'variable'); + return $model->toArray(); } } diff --git a/app/Transformers/Api/Application/SubuserTransformer.php b/app/Transformers/Api/Application/SubuserTransformer.php index 7927272ca..7b3ef7c0b 100644 --- a/app/Transformers/Api/Application/SubuserTransformer.php +++ b/app/Transformers/Api/Application/SubuserTransformer.php @@ -3,22 +3,20 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Subuser; -use Pterodactyl\Models\Permission; +use League\Fractal\Resource\Item; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class SubuserTransformer extends BaseTransformer +class SubuserTransformer extends Transformer { /** * List of resources that can be included. - * - * @var array */ - protected $availableIncludes = ['user', 'server']; + protected array $availableIncludes = ['server', 'user']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -27,59 +25,40 @@ class SubuserTransformer extends BaseTransformer /** * Return a transformed Subuser model that can be consumed by external services. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @return array */ - public function transform(Subuser $subuser): array + public function transform(Subuser $model): array { return [ - 'id' => $subuser->id, - 'user_id' => $subuser->user_id, - 'server_id' => $subuser->server_id, - 'permissions' => $subuser->permissions->map(function (Permission $permission) { - return $permission->permission; - }), - 'created_at' => $this->formatTimestamp($subuser->created_at), - 'updated_at' => $this->formatTimestamp($subuser->updated_at), + 'id' => $model->id, + 'user_id' => $model->user_id, + 'server_id' => $model->server_id, + 'permissions' => $model->permissions, + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } /** - * Return a generic item of user for this subuser. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return a generic item of server for this subuser. */ - public function includeUser(Subuser $subuser) + public function includeServer(Subuser $subuser): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_USERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { return $this->null(); } - $subuser->loadMissing('user'); - - return $this->item($subuser->getRelation('user'), $this->makeTransformer(UserTransformer::class), 'user'); + return $this->item($subuser->server, new ServerTransformer()); } /** - * Return a generic item of server for this subuser. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * Return a generic item of user for this subuser. */ - public function includeServer(Subuser $subuser) + public function includeUser(Subuser $subuser): Item|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { return $this->null(); } - $subuser->loadMissing('server'); - - return $this->item($subuser->getRelation('server'), $this->makeTransformer(ServerTransformer::class), 'server'); + return $this->item($subuser->user, new UserTransformer()); } } diff --git a/app/Transformers/Api/Application/UserTransformer.php b/app/Transformers/Api/Application/UserTransformer.php index 2e55bbb1b..2a3065654 100644 --- a/app/Transformers/Api/Application/UserTransformer.php +++ b/app/Transformers/Api/Application/UserTransformer.php @@ -3,21 +3,20 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\User; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; use Pterodactyl\Services\Acl\Api\AdminAcl; +use Pterodactyl\Transformers\Api\Transformer; -class UserTransformer extends BaseTransformer +class UserTransformer extends Transformer { /** * List of resources that can be included. - * - * @var array */ - protected $availableIncludes = ['servers']; + protected array $availableIncludes = ['servers']; /** * Return the resource name for the JSONAPI output. - * - * @return string */ public function getResourceName(): string { @@ -26,44 +25,35 @@ class UserTransformer extends BaseTransformer /** * Return a transformed User model that can be consumed by external services. - * - * @param \Pterodactyl\Models\User $user - * @return array */ - public function transform(User $user): array + public function transform(User $model): array { return [ - 'id' => $user->id, - 'external_id' => $user->external_id, - 'uuid' => $user->uuid, - 'username' => $user->username, - 'email' => $user->email, - 'first_name' => $user->name_first, - 'last_name' => $user->name_last, - 'language' => $user->language, - 'root_admin' => (bool) $user->root_admin, - '2fa' => (bool) $user->use_totp, - 'created_at' => $this->formatTimestamp($user->created_at), - 'updated_at' => $this->formatTimestamp($user->updated_at), + 'id' => $model->id, + 'external_id' => $model->external_id, + 'uuid' => $model->uuid, + 'username' => $model->username, + 'email' => $model->email, + 'language' => $model->language, + 'root_admin' => (bool) $model->root_admin, + '2fa' => (bool) $model->use_totp, + 'avatar_url' => $model->avatar_url, + 'admin_role_id' => $model->admin_role_id, + 'role_name' => $model->admin_role_name, + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), ]; } /** * Return the servers associated with this user. - * - * @param \Pterodactyl\Models\User $user - * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeServers(User $user) + public function includeServers(User $user): Collection|NullResource { - if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { return $this->null(); } - $user->loadMissing('servers'); - - return $this->collection($user->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server'); + return $this->collection($user->servers, new ServerTransformer()); } } diff --git a/app/Transformers/Api/Client/AccountTransformer.php b/app/Transformers/Api/Client/AccountTransformer.php new file mode 100644 index 000000000..3a78792d6 --- /dev/null +++ b/app/Transformers/Api/Client/AccountTransformer.php @@ -0,0 +1,31 @@ + $model->id, + 'admin' => $model->root_admin, + 'username' => $model->username, + 'email' => $model->email, + 'language' => $model->language, + ]; + } +} diff --git a/app/Transformers/Api/Client/ActivityLogTransformer.php b/app/Transformers/Api/Client/ActivityLogTransformer.php new file mode 100644 index 000000000..dc0be9613 --- /dev/null +++ b/app/Transformers/Api/Client/ActivityLogTransformer.php @@ -0,0 +1,121 @@ + sha1($model->id), + 'batch' => $model->batch, + 'event' => $model->event, + 'is_api' => !is_null($model->api_key_id), + 'ip' => $this->canViewIP($model->actor) ? $model->ip : null, + 'description' => $model->description, + 'properties' => $this->properties($model), + 'has_additional_metadata' => $this->hasAdditionalMetadata($model), + 'timestamp' => $model->timestamp->toAtomString(), + ]; + } + + public function includeActor(ActivityLog $model): Item|NullResource + { + if (!$model->actor instanceof User) { + return $this->null(); + } + + return $this->item($model->actor, new UserTransformer()); + } + + /** + * Transforms any array values in the properties into a countable field for easier + * use within the translation outputs. + */ + protected function properties(ActivityLog $model): object + { + if (!$model->properties || $model->properties->isEmpty()) { + return (object) []; + } + + $properties = $model->properties + ->mapWithKeys(function ($value, $key) use ($model) { + if ($key === 'ip' && !optional($model->actor)->is($this->request->user())) { + return [$key => '[hidden]']; + } + + if (!is_array($value)) { + // Perform some directory normalization at this point. + if ($key === 'directory') { + $value = str_replace('//', '/', '/' . trim($value, '/') . '/'); + } + + return [$key => $value]; + } + + return [$key => $value, "{$key}_count" => count($value)]; + }); + + $keys = $properties->keys()->filter(fn ($key) => Str::endsWith($key, '_count'))->values(); + if ($keys->containsOneItem()) { + $properties = $properties->merge(['count' => $properties->get($keys[0])])->except($keys[0]); + } + + return (object) $properties->toArray(); + } + + /** + * Determines if there are any log properties that we've not already exposed + * in the response language string and that are not just the IP address or + * the browser useragent. + * + * This is used by the front-end to selectively display an "additional metadata" + * button that is pointless if there is nothing the user can't already see from + * the event description. + */ + protected function hasAdditionalMetadata(ActivityLog $model): bool + { + if (is_null($model->properties) || $model->properties->isEmpty()) { + return false; + } + + $str = trans('activity.' . str_replace(':', '.', $model->event)); + preg_match_all('/:(?[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches); + + $exclude = array_merge($matches['key'], ['ip', 'useragent', 'using_sftp']); + foreach ($model->properties->keys() as $key) { + if (!in_array($key, $exclude, true)) { + return true; + } + } + + return false; + } + + /** + * Determines if the user can view the IP address in the output either because they are the + * actor that performed the action, or because they are an administrator on the Panel. + */ + protected function canViewIP(Model $actor = null): bool + { + return optional($actor)->is($this->request->user()) || $this->request->user()->root_admin; + } +} diff --git a/app/Transformers/Api/Client/AllocationTransformer.php b/app/Transformers/Api/Client/AllocationTransformer.php new file mode 100644 index 000000000..f39f2e397 --- /dev/null +++ b/app/Transformers/Api/Client/AllocationTransformer.php @@ -0,0 +1,29 @@ + $model->id, + 'ip' => $model->ip, + 'ip_alias' => $model->ip_alias, + 'port' => $model->port, + 'notes' => $model->notes, + 'is_default' => $model->server->allocation_id === $model->id, + ]; + } +} diff --git a/app/Transformers/Api/Client/ApiKeyTransformer.php b/app/Transformers/Api/Client/ApiKeyTransformer.php new file mode 100644 index 000000000..022b83ffc --- /dev/null +++ b/app/Transformers/Api/Client/ApiKeyTransformer.php @@ -0,0 +1,31 @@ + $model->identifier, + 'description' => $model->memo, + 'allowed_ips' => $model->allowed_ips, + 'last_used_at' => $model->last_used_at ? $model->last_used_at->toAtomString() : null, + 'created_at' => $model->created_at->toAtomString(), + ]; + } +} diff --git a/app/Transformers/Api/Client/BackupTransformer.php b/app/Transformers/Api/Client/BackupTransformer.php new file mode 100644 index 000000000..6797c40dd --- /dev/null +++ b/app/Transformers/Api/Client/BackupTransformer.php @@ -0,0 +1,29 @@ + $model->uuid, + 'is_successful' => $model->is_successful, + 'is_locked' => $model->is_locked, + 'name' => $model->name, + 'ignored_files' => $model->ignored_files, + 'checksum' => $model->checksum, + 'bytes' => $model->bytes, + 'created_at' => self::formatTimestamp($model->created_at), + 'completed_at' => self::formatTimestamp($model->completed_at), + ]; + } +} diff --git a/app/Transformers/Api/Client/BaseClientTransformer.php b/app/Transformers/Api/Client/BaseClientTransformer.php deleted file mode 100644 index faa1abbed..000000000 --- a/app/Transformers/Api/Client/BaseClientTransformer.php +++ /dev/null @@ -1,74 +0,0 @@ -user; - } - - /** - * Set the user model of the user requesting this transformation. - * - * @param \Pterodactyl\Models\User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } - - /** - * Determine if the API key loaded onto the transformer has permission - * to access a different resource. This is used when including other - * models on a transformation request. - * - * @param string $ability - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - protected function authorize(string $ability, Server $server = null): bool - { - Assert::isInstanceOf($server, Server::class); - - return $this->getUser()->can($ability, [$server]); - } - - /** - * Create a new instance of the transformer and pass along the currently - * set API key. - * - * @param string $abstract - * @param array $parameters - * @return self - * - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - protected function makeTransformer(string $abstract, array $parameters = []) - { - $transformer = parent::makeTransformer($abstract, $parameters); - - if (! $transformer instanceof self) { - throw new InvalidTransformerLevelException('Calls to ' . __METHOD__ . ' must return a transformer that is an instance of ' . __CLASS__); - } - - return $transformer; - } -} diff --git a/app/Transformers/Api/Client/DatabaseTransformer.php b/app/Transformers/Api/Client/DatabaseTransformer.php new file mode 100644 index 000000000..5ad899ad9 --- /dev/null +++ b/app/Transformers/Api/Client/DatabaseTransformer.php @@ -0,0 +1,66 @@ +encrypter = $encrypter; + $this->hashids = $hashids; + } + + public function getResourceName(): string + { + return Database::RESOURCE_NAME; + } + + public function transform(Database $model): array + { + $model->loadMissing('host'); + + return [ + 'id' => $this->hashids->encode($model->id), + 'host' => [ + 'address' => $model->host->host, + 'port' => $model->host->port, + ], + 'name' => $model->database, + 'username' => $model->username, + 'connections_from' => $model->remote, + 'max_connections' => $model->max_connections, + ]; + } + + /** + * Include the database password in the request. + */ + public function includePassword(Database $database): Item|NullResource + { + if ($this->user()->cannot(Permission::ACTION_DATABASE_VIEW_PASSWORD, $database->server)) { + return $this->null(); + } + + return $this->item($database, function (Database $model) { + return [ + 'password' => $this->encrypter->decrypt($model->password), + ]; + }, 'database_password'); + } +} diff --git a/app/Transformers/Api/Client/EggTransformer.php b/app/Transformers/Api/Client/EggTransformer.php new file mode 100644 index 000000000..493958da5 --- /dev/null +++ b/app/Transformers/Api/Client/EggTransformer.php @@ -0,0 +1,25 @@ + $model->uuid, + 'name' => $model->name, + ]; + } +} diff --git a/app/Transformers/Api/Client/EggVariableTransformer.php b/app/Transformers/Api/Client/EggVariableTransformer.php new file mode 100644 index 000000000..ea0ead081 --- /dev/null +++ b/app/Transformers/Api/Client/EggVariableTransformer.php @@ -0,0 +1,34 @@ +user_viewable) { + throw new \BadMethodCallException('Cannot transform a hidden egg variable in a client transformer.'); + } + + return [ + 'name' => $model->name, + 'description' => $model->description, + 'env_variable' => $model->env_variable, + 'default_value' => $model->default_value, + 'server_value' => $model->server_value, + 'is_editable' => $model->user_editable, + 'rules' => $model->rules, + ]; + } +} diff --git a/app/Transformers/Api/Client/FileObjectTransformer.php b/app/Transformers/Api/Client/FileObjectTransformer.php new file mode 100644 index 000000000..94a966000 --- /dev/null +++ b/app/Transformers/Api/Client/FileObjectTransformer.php @@ -0,0 +1,33 @@ + Arr::get($model, 'name'), + 'mode' => Arr::get($model, 'mode'), + 'mode_bits' => Arr::get($model, 'mode_bits'), + 'size' => Arr::get($model, 'size'), + 'is_file' => Arr::get($model, 'file', true), + 'is_symlink' => Arr::get($model, 'symlink', false), + 'mimetype' => Arr::get($model, 'mime', 'application/octet-stream'), + 'created_at' => Carbon::parse(Arr::get($model, 'created', ''))->toAtomString(), + 'modified_at' => Carbon::parse(Arr::get($model, 'modified', ''))->toAtomString(), + ]; + } +} diff --git a/app/Transformers/Api/Client/ScheduleTransformer.php b/app/Transformers/Api/Client/ScheduleTransformer.php new file mode 100644 index 000000000..735e8fdd8 --- /dev/null +++ b/app/Transformers/Api/Client/ScheduleTransformer.php @@ -0,0 +1,55 @@ + $model->id, + 'name' => $model->name, + 'cron' => [ + 'day_of_week' => $model->cron_day_of_week, + 'day_of_month' => $model->cron_day_of_month, + 'month' => $model->cron_month, + 'hour' => $model->cron_hour, + 'minute' => $model->cron_minute, + ], + 'is_active' => $model->is_active, + 'is_processing' => $model->is_processing, + 'only_when_online' => $model->only_when_online, + 'last_run_at' => self::formatTimestamp($model->last_run_at), + 'next_run_at' => self::formatTimestamp($model->next_run_at), + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), + ]; + } + + /** + * Allows attaching the tasks specific to the schedule in the response. + */ + public function includeTasks(Schedule $model): Collection + { + return $this->collection($model->tasks, new TaskTransformer()); + } +} diff --git a/app/Transformers/Api/Client/ServerTransformer.php b/app/Transformers/Api/Client/ServerTransformer.php index 6816d6d74..56ad04860 100644 --- a/app/Transformers/Api/Client/ServerTransformer.php +++ b/app/Transformers/Api/Client/ServerTransformer.php @@ -2,13 +2,23 @@ namespace Pterodactyl\Transformers\Api\Client; +use Pterodactyl\Models\Egg; use Pterodactyl\Models\Server; +use League\Fractal\Resource\Item; +use Pterodactyl\Models\Allocation; +use Pterodactyl\Models\Permission; +use Illuminate\Container\Container; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\NullResource; +use Pterodactyl\Transformers\Api\Transformer; +use Pterodactyl\Services\Servers\StartupCommandService; -class ServerTransformer extends BaseClientTransformer +class ServerTransformer extends Transformer { - /** - * @return string - */ + protected array $defaultIncludes = ['allocations', 'variables']; + + protected array $availableIncludes = ['egg', 'subusers']; + public function getResourceName(): string { return Server::RESOURCE_NAME; @@ -17,17 +27,26 @@ class ServerTransformer extends BaseClientTransformer /** * Transform a server model into a representation that can be returned * to a client. - * - * @param \Pterodactyl\Models\Server $server - * @return array */ public function transform(Server $server): array { + /** @var \Pterodactyl\Services\Servers\StartupCommandService $service */ + $service = Container::getInstance()->make(StartupCommandService::class); + + $user = $this->request->user(); + return [ - 'server_owner' => $this->getKey()->user_id === $server->owner_id, + 'server_owner' => $user->id === $server->owner_id, 'identifier' => $server->uuidShort, + 'internal_id' => $server->id, 'uuid' => $server->uuid, 'name' => $server->name, + 'node' => $server->node->name, + 'is_node_under_maintenance' => $server->node->isUnderMaintenance(), + 'sftp_details' => [ + 'ip' => $server->node->fqdn, + 'port' => $server->node->public_port_sftp, + ], 'description' => $server->description, 'limits' => [ 'memory' => $server->memory, @@ -35,11 +54,73 @@ class ServerTransformer extends BaseClientTransformer 'disk' => $server->disk, 'io' => $server->io, 'cpu' => $server->cpu, + 'threads' => $server->threads, + 'oom_killer' => $server->oom_killer, ], + 'invocation' => $service->handle($server, !$user->can(Permission::ACTION_STARTUP_READ, $server)), + 'docker_image' => $server->image, + 'egg_features' => $server->egg->inherit_features, 'feature_limits' => [ 'databases' => $server->database_limit, 'allocations' => $server->allocation_limit, + 'backups' => $server->backup_limit, ], + 'status' => $server->status, + 'is_transferring' => !is_null($server->transfer), ]; } + + /** + * Returns the allocations associated with this server. + */ + public function includeAllocations(Server $server): Collection + { + $transformer = new AllocationTransformer(); + + $user = $this->request->user(); + // While we include this permission, we do need to actually handle it slightly different here + // for the purpose of keeping things functionally working. If the user doesn't have read permissions + // for the allocations we'll only return the primary server allocation, and any notes associated + // with it will be hidden. + // + // This allows us to avoid too much permission regression, without also hiding information that + // is generally needed for the frontend to make sense when browsing or searching results. + if (!$user->can(Permission::ACTION_ALLOCATION_READ, $server)) { + $primary = clone $server->allocation; + $primary->notes = null; + + return $this->collection([$primary], $transformer); + } + + return $this->collection($server->allocations, $transformer); + } + + public function includeVariables(Server $server): Collection|NullResource + { + if (!$this->request->user()->can(Permission::ACTION_STARTUP_READ, $server)) { + return $this->null(); + } + + return $this->collection($server->variables->where('user_viewable', true), new EggVariableTransformer()); + } + + /** + * Returns the egg associated with this server. + */ + public function includeEgg(Server $server): Item + { + return $this->item($server->egg, new EggTransformer()); + } + + /** + * Returns the subusers associated with this server. + */ + public function includeSubusers(Server $server): Collection|NullResource + { + if (!$this->request->user()->can(Permission::ACTION_USER_READ, $server)) { + return $this->null(); + } + + return $this->collection($server->subusers, new SubuserTransformer()); + } } diff --git a/app/Transformers/Api/Client/StatsTransformer.php b/app/Transformers/Api/Client/StatsTransformer.php index 01d8e3f20..0ae696cc3 100644 --- a/app/Transformers/Api/Client/StatsTransformer.php +++ b/app/Transformers/Api/Client/StatsTransformer.php @@ -2,29 +2,11 @@ namespace Pterodactyl\Transformers\Api\Client; -use Pterodactyl\Models\Server; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface; +use Illuminate\Support\Arr; +use Pterodactyl\Transformers\Api\Transformer; -class StatsTransformer extends BaseClientTransformer +class StatsTransformer extends Transformer { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - private $repository; - - /** - * Perform dependency injection. - * - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $repository - */ - public function handle(ServerRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * @return string - */ public function getResourceName(): string { return 'stats'; @@ -33,56 +15,20 @@ class StatsTransformer extends BaseClientTransformer /** * Transform stats from the daemon into a result set that can be used in * the client API. - * - * @param \Pterodactyl\Models\Server $model - * @return array */ - public function transform(Server $model) + public function transform(array $model): array { - try { - $stats = $this->repository->setServer($model)->details(); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } - - $object = json_decode($stats->getBody()->getContents()); - return [ - 'state' => $this->transformState(object_get($object, 'status', 0)), - 'memory' => [ - 'current' => round(object_get($object, 'proc.memory.total', 0) / 1024 / 1024), - 'limit' => floatval($model->memory), - ], - 'cpu' => [ - 'current' => object_get($object, 'proc.cpu.total', 0), - 'cores' => object_get($object, 'proc.cpu.cores', []), - 'limit' => floatval($model->cpu), - ], - 'disk' => [ - 'current' => round(object_get($object, 'proc.disk.used', 0)), - 'limit' => floatval($model->disk), + 'current_state' => Arr::get($model, 'state', 'stopped'), + 'is_suspended' => Arr::get($model, 'is_suspended', false), + 'resources' => [ + 'memory_bytes' => Arr::get($model, 'utilization.memory_bytes', 0), + 'cpu_absolute' => Arr::get($model, 'utilization.cpu_absolute', 0), + 'disk_bytes' => Arr::get($model, 'utilization.disk_bytes', 0), + 'network_rx_bytes' => Arr::get($model, 'utilization.network.rx_bytes', 0), + 'network_tx_bytes' => Arr::get($model, 'utilization.network.tx_bytes', 0), + 'uptime' => Arr::get($model, 'utilization.uptime', 0), ], ]; } - - /** - * Transform the state returned by the daemon into a human readable string. - * - * @param int $state - * @return string - */ - private function transformState(int $state): string - { - switch ($state) { - case 1: - return 'on'; - case 2: - return 'starting'; - case 3: - return 'stopping'; - case 0: - default: - return 'off'; - } - } } diff --git a/app/Transformers/Api/Client/SubuserTransformer.php b/app/Transformers/Api/Client/SubuserTransformer.php new file mode 100644 index 000000000..4d26ae655 --- /dev/null +++ b/app/Transformers/Api/Client/SubuserTransformer.php @@ -0,0 +1,28 @@ +transform($model->user), + ['permissions' => $model->permissions] + ); + } +} diff --git a/app/Transformers/Api/Client/TaskTransformer.php b/app/Transformers/Api/Client/TaskTransformer.php new file mode 100644 index 000000000..84054651e --- /dev/null +++ b/app/Transformers/Api/Client/TaskTransformer.php @@ -0,0 +1,35 @@ + $model->id, + 'sequence_id' => $model->sequence_id, + 'action' => $model->action, + 'payload' => $model->payload, + 'time_offset' => $model->time_offset, + 'is_queued' => $model->is_queued, + 'continue_on_failure' => $model->continue_on_failure, + 'created_at' => self::formatTimestamp($model->created_at), + 'updated_at' => self::formatTimestamp($model->updated_at), + ]; + } +} diff --git a/app/Transformers/Api/Client/UserSSHKeyTransformer.php b/app/Transformers/Api/Client/UserSSHKeyTransformer.php new file mode 100644 index 000000000..a13370eb5 --- /dev/null +++ b/app/Transformers/Api/Client/UserSSHKeyTransformer.php @@ -0,0 +1,27 @@ + $model->name, + 'fingerprint' => $model->fingerprint, + 'public_key' => $model->public_key, + 'created_at' => self::formatTimestamp($model->created_at), + ]; + } +} diff --git a/app/Transformers/Api/Client/UserTransformer.php b/app/Transformers/Api/Client/UserTransformer.php new file mode 100644 index 000000000..c076e6b99 --- /dev/null +++ b/app/Transformers/Api/Client/UserTransformer.php @@ -0,0 +1,33 @@ + $model->uuid, + 'username' => $model->username, + 'email' => $model->email, + 'image' => $model->avatar_url, + '2fa_enabled' => $model->use_totp, + 'created_at' => self::formatTimestamp($model->created_at), + ]; + } +} diff --git a/app/Transformers/Api/Transformer.php b/app/Transformers/Api/Transformer.php new file mode 100644 index 000000000..89051d77f --- /dev/null +++ b/app/Transformers/Api/Transformer.php @@ -0,0 +1,154 @@ +request = Container::getInstance()->make('request'); + + if (method_exists($this, 'handle')) { + Container::getInstance()->call([$this, 'handle']); + } + } + + /** + * Returns the resource name for the transformed item. + */ + abstract public function getResourceName(): string; + + /** + * Returns the authorized user for the request. + */ + protected function user(): User + { + return $this->request->user(); + } + + /** + * Determines if the user making this request is authorized to access the given + * resource on the API. This is used when requested included items to ensure that + * the user and key are authorized to see the result. + * + * TODO: implement this with the new API key formats. + */ + protected function authorize(string $resource): bool + { + return $this->request->user() instanceof User; + } + + /** + * {@inheritDoc} + * + * @param mixed $data + * @param callable|\League\Fractal\TransformerAbstract $transformer + */ + protected function item($data, $transformer, ?string $resourceKey = null): Item + { + if (!$transformer instanceof \Closure) { + self::assertSameNamespace($transformer); + } + + $item = parent::item($data, $transformer, $resourceKey); + + if (!$item->getResourceKey() && method_exists($transformer, 'getResourceName')) { + $item->setResourceKey($transformer->getResourceName()); + } + + return $item; + } + + /** + * {@inheritDoc} + * + * @param mixed $data + * @param callable|\League\Fractal\TransformerAbstract $transformer + */ + protected function collection($data, $transformer, ?string $resourceKey = null): Collection + { + if (!$transformer instanceof \Closure) { + self::assertSameNamespace($transformer); + } + + $collection = parent::collection($data, $transformer, $resourceKey); + + if (!$collection->getResourceKey() && method_exists($transformer, 'getResourceName')) { + $collection->setResourceKey($transformer->getResourceName()); + } + + return $collection; + } + + /** + * Sets the default timezone to use for transformed responses. Pass a null value + * to return back to the default timezone (UTC). + */ + public static function setTimezone(string $tz = null) + { + static::$timezone = $tz ?? 'UTC'; + } + + /** + * Asserts that the given transformer is the same base namespace as the class that + * implements this abstract transformer class. This prevents a client or application + * transformer from unintentionally transforming a resource using an unexpected type. + * + * @param callable|\League\Fractal\TransformerAbstract $transformer + */ + protected static function assertSameNamespace($transformer) + { + Assert::subclassOf($transformer, TransformerAbstract::class); + + $namespace = substr(get_class($transformer), 0, strlen(class_basename($transformer)) * -1); + $expected = substr(static::class, 0, strlen(class_basename(static::class)) * -1); + + Assert::same($namespace, $expected, 'Cannot invoke a new transformer (%s) that is not in the same namespace (%s).'); + } + + /** + * Returns an ISO-8601 formatted timestamp to use in API responses. This + * time is returned in the default transformer timezone if no timezone value + * is provided. + * + * If no time is provided a null value is returned. + * + * @param string|\DateTimeInterface|null $timestamp + */ + protected static function formatTimestamp($timestamp, string $tz = null): ?string + { + if (empty($timestamp)) { + return null; + } + + if ($timestamp instanceof \DateTimeInterface) { + $value = CarbonImmutable::instance($timestamp); + } else { + $value = CarbonImmutable::createFromFormat(CarbonInterface::DEFAULT_TO_STRING_FORMAT, $timestamp); + } + + return $value->setTimezone($tz ?? self::$timezone)->toAtomString(); + } +} diff --git a/app/Transformers/Daemon/ApiKeyTransformer.php b/app/Transformers/Daemon/ApiKeyTransformer.php deleted file mode 100644 index 7f1d7427b..000000000 --- a/app/Transformers/Daemon/ApiKeyTransformer.php +++ /dev/null @@ -1,76 +0,0 @@ -repository = $repository; - $this->keyRepository = $keyRepository; - } - - /** - * Return a listing of servers that a daemon key can access. - * - * @param \Pterodactyl\Models\DaemonKey $key - * @return array - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function transform(DaemonKey $key) - { - $this->keyRepository->loadServerAndUserRelations($key); - - if ($key->user_id === $key->getRelation('server')->owner_id || $key->getRelation('user')->root_admin) { - return [ - 'id' => $key->getRelation('server')->uuid, - 'is_temporary' => true, - 'expires_in' => max(Carbon::now()->diffInSeconds($key->expires_at, false), 0), - 'permissions' => ['s:*'], - ]; - } - - $subuser = $this->repository->getWithPermissionsUsingUserAndServer($key->user_id, $key->server_id); - - $permissions = $subuser->getRelation('permissions')->pluck('permission')->toArray(); - $mappings = Permission::getPermissions(true); - $daemonPermissions = ['s:console']; - - foreach ($permissions as $permission) { - if (! is_null(array_get($mappings, $permission))) { - $daemonPermissions[] = array_get($mappings, $permission); - } - } - - return [ - 'id' => $key->getRelation('server')->uuid, - 'is_temporary' => true, - 'expires_in' => max(Carbon::now()->diffInSeconds($key->expires_at, false), 0), - 'permissions' => $daemonPermissions, - ]; - } -} diff --git a/app/helpers.php b/app/helpers.php index bfb12cc7a..59aa7166a 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -1,67 +1,29 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ -if (! function_exists('human_readable')) { - /** - * Generate a human-readable filesize for a given file path. - * - * @param string $path - * @param int $precision - * @return string - */ - function human_readable($path, $precision = 2) - { - if (is_numeric($path)) { - $i = 0; - while (($path / 1024) > 0.9) { - $path = $path / 1024; - $i++; - } - return round($path, $precision) . ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i]; - } - - return app('file')->humanReadableSize($path, $precision); - } -} - -if (! function_exists('is_digit')) { +if (!function_exists('is_digit')) { /** * Deal with normal (and irritating) PHP behavior to determine if * a value is a non-float positive integer. - * - * @param mixed $value - * @return bool */ - function is_digit($value) + function is_digit(mixed $value): bool { - return is_bool($value) ? false : ctype_digit(strval($value)); + return !is_bool($value) && ctype_digit(strval($value)); } } -if (! function_exists('object_get_strict')) { +if (!function_exists('object_get_strict')) { /** * Get an object using dot notation. An object key with a value of null is still considered valid * and will not trigger the response of a default value (unlike object_get). - * - * @param object $object - * @param string $key - * @param null $default - * @return mixed */ - function object_get_strict($object, $key, $default = null) + function object_get_strict(object $object, ?string $key, $default = null): mixed { if (is_null($key) || trim($key) == '') { return $object; } foreach (explode('.', $key) as $segment) { - if (! is_object($object) || ! property_exists($object, $segment)) { + if (!is_object($object) || !property_exists($object, $segment)) { return value($default); } diff --git a/artisan b/artisan index 5c23e2e24..240ab7a91 100755 --- a/artisan +++ b/artisan @@ -11,13 +11,13 @@ define('LARAVEL_START', microtime(true)); | Composer provides a convenient, automatically generated class loader | for our application. We just need to utilize it! We'll require it | into the script here so that we do not have to worry about the -| loading of any our classes "manually". Feels great to relax. +| loading of our classes manually. It's great to relax. | */ -require __DIR__.'/vendor/autoload.php'; +require __DIR__ . '/vendor/autoload.php'; -$app = require_once __DIR__.'/bootstrap/app.php'; +$app = require_once __DIR__ . '/bootstrap/app.php'; /* |-------------------------------------------------------------------------- @@ -33,8 +33,8 @@ $app = require_once __DIR__.'/bootstrap/app.php'; $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); $status = $kernel->handle( - $input = new Symfony\Component\Console\Input\ArgvInput, - new Symfony\Component\Console\Output\ConsoleOutput + $input = new Symfony\Component\Console\Input\ArgvInput(), + new Symfony\Component\Console\Output\ConsoleOutput() ); /* diff --git a/bootstrap/app.php b/bootstrap/app.php index f35c15929..ee5f1bae2 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -12,7 +12,7 @@ */ $app = new Illuminate\Foundation\Application( - realpath(__DIR__ . '/../') + $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) ); /* diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100755 new mode 100644 diff --git a/bootstrap/tests.php b/bootstrap/tests.php index 276786610..66f23147d 100644 --- a/bootstrap/tests.php +++ b/bootstrap/tests.php @@ -1,5 +1,7 @@ make(Kernel::class); */ $kernel->bootstrap(); -$output = new ConsoleOutput; +// Register the collision service provider so that errors during the test +// setup process are output nicely. +(new Provider())->register(); + +$output = new ConsoleOutput(); + +$prefix = 'database.connections.' . config('database.default'); +if (!Str::contains(config("$prefix.database"), 'test')) { + $output->writeln(PHP_EOL . 'Cannot run test process against non-testing database.'); + $output->writeln(PHP_EOL . 'Environment is currently pointed at: "' . config("$prefix.database") . '".'); + exit(1); +} /* * Perform database migrations and reseeding before continuing with * running the tests. */ -$output->writeln(PHP_EOL . 'Refreshing database for Integration tests...'); -$kernel->call('migrate:fresh', ['--database' => 'testing']); +if (!env('SKIP_MIGRATIONS')) { + $output->writeln(PHP_EOL . 'Refreshing database for Integration tests...'); + $kernel->call('migrate:fresh'); -$output->writeln('Seeding database for Integration tests...' . PHP_EOL); -$kernel->call('db:seed', ['--database' => 'testing']); + $output->writeln('Seeding database for Integration tests...' . PHP_EOL); + $kernel->call('db:seed'); + + $output->writeln('Database configured, running Integration tests...' . PHP_EOL); +} else { + $output->writeln(PHP_EOL . 'Skipping database migrations...' . PHP_EOL); +} diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index fdc1d707a..000000000 --- a/codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -coverage: - status: - project: off - patch: off -comment: false diff --git a/composer.json b/composer.json index e1bc76f5e..40441a7d1 100644 --- a/composer.json +++ b/composer.json @@ -3,88 +3,106 @@ "description": "The free, open-source game management panel. Supporting Minecraft, Spigot, BungeeCord, and SRCDS servers.", "license": "MIT", "authors": [ + { + "name": "Matthew Penner", + "email": "matthew@pterodactyl.io", + "homepage": "https://github.com/matthewpi", + "role": "Lead Developer" + }, { "name": "Dane Everitt", "email": "dane@daneeveritt.com", "homepage": "https://github.com/DaneEveritt", - "role": "Lead Developer" + "role": "Developer" } ], "require": { - "php": ">=7.2", + "php": "^8.1 || ^8.2", + "ext-json": "*", "ext-mbstring": "*", + "ext-pdo": "*", "ext-pdo_mysql": "*", + "ext-posix": "*", "ext-zip": "*", - "appstract/laravel-blade-directives": "^0.7", - "aws/aws-sdk-php": "^3.48", - "cakephp/chronos": "^1.1", - "doctrine/dbal": "^2.5", - "fideloper/proxy": "^4.0", - "guzzlehttp/guzzle": "^6.3", - "hashids/hashids": "^3.0", - "igaster/laravel-theme": "^2.0.6", - "laracasts/utilities": "^3.0", - "laravel/framework": "~5.7.14", - "laravel/tinker": "^1.0", - "lord/laroute": "^2.4", - "matriphe/iso-639": "^1.2", - "nesbot/carbon": "^1.22", - "pragmarx/google2fa": "^5.0", - "predis/predis": "^1.1", - "prologue/alerts": "^0.4", - "ramsey/uuid": "^3.7", - "s1lentium/iptools": "^1.1", - "sofa/eloquence-base": "v5.6.2", - "sofa/eloquence-validable": "v5.6", - "spatie/laravel-fractal": "^5.4", - "staudenmeir/belongs-to-through": "~2.3.0", - "webmozart/assert": "^1.2" + "aws/aws-sdk-php": "~3.260.1", + "doctrine/dbal": "~3.6.0", + "guzzlehttp/guzzle": "~7.5.0", + "hashids/hashids": "~5.0.0", + "laracasts/utilities": "~3.2.2", + "laravel/framework": "~10.1.3", + "laravel/helpers": "~1.6.0", + "laravel/sanctum": "~3.2.1", + "laravel/tinker": "~2.8.1", + "laravel/ui": "~4.2.1", + "lcobucci/jwt": "~4.3.0", + "league/flysystem-aws-s3-v3": "~3.12.2", + "league/flysystem-memory": "~3.10.3", + "matriphe/iso-639": "~1.2", + "phpseclib/phpseclib": "~3.0.18", + "pragmarx/google2fa": "~8.0.0", + "predis/predis": "~2.1.1", + "prologue/alerts": "~1.1.0", + "psr/cache": "~3.0.0", + "s1lentium/iptools": "~1.2.0", + "spatie/laravel-fractal": "~6.0.3", + "spatie/laravel-query-builder": "~5.1.2", + "staudenmeir/belongs-to-through": "~2.13", + "symfony/http-client": "~6.2.6", + "symfony/mailgun-mailer": "~6.2.5", + "symfony/postmark-mailer": "~6.2.5", + "symfony/yaml": "~6.2.5", + "webmozart/assert": "~1.11.0" }, "require-dev": { - "barryvdh/laravel-debugbar": "^3.2", - "barryvdh/laravel-ide-helper": "^2.5", - "codedungeon/phpunit-result-printer": "^0.17.1", - "filp/whoops": "^2.1", - "friendsofphp/php-cs-fixer": "^2.15.1", - "fzaninotto/faker": "^1.6", - "mockery/mockery": "^1.0", - "nunomaduro/collision": "^2.0", - "php-mock/php-mock-phpunit": "^2.1", - "phpunit/phpunit": "~7.0" + "barryvdh/laravel-ide-helper": "~2.13.0", + "fakerphp/faker": "~1.21.0", + "friendsofphp/php-cs-fixer": "~3.14.4", + "itsgoingd/clockwork": "~5.1.12", + "laravel/sail": "~1.21.0", + "mockery/mockery": "~1.5.1", + "nunomaduro/collision": "~7.0.5", + "nunomaduro/larastan": "~2.4.1", + "phpstan/phpstan": "~1.10.1", + "phpunit/phpunit": "~10.0.11", + "spatie/laravel-ignition": "~2.0.0" }, "autoload": { - "classmap": [ - "database" - ], "files": [ "app/helpers.php" ], "psr-4": { - "Pterodactyl\\": "app/" + "Pterodactyl\\": "app/", + "Database\\Factories\\": "database/Factories/", + "Database\\Seeders\\": "database/Seeders/" } }, "autoload-dev": { "psr-4": { - "Pterodactyl\\Tests\\Integration\\": "tests/Integration", - "Tests\\": "tests/" + "Pterodactyl\\Tests\\": "tests/" } }, "scripts": { + "cs:fix": "php-cs-fixer fix", + "cs:check": "php-cs-fixer fix --dry-run --diff --verbose", + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi || true" + ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "post-create-project-cmd": [ - "@php artisan key:generate" - ], - "post-autoload-dump": [ - "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", - "@php artisan package:discover" + "@php artisan key:generate --ansi" ] }, - "prefer-stable": true, "config": { + "optimize-autoloader": true, "preferred-install": "dist", "sort-packages": true, - "optimize-autoloader": false - } + "platform": { + "php": "8.1.0" + } + }, + "minimum-stability": "stable", + "prefer-stable": true } diff --git a/composer.lock b/composer.lock index 714d18675..db0deff8c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,98 +4,101 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "175a2431d23dd32f79b42bbc8030ace1", + "content-hash": "ea6b61d325c0e7489fb7997f0b08077d", "packages": [ { - "name": "appstract/laravel-blade-directives", - "version": "0.7.1", + "name": "aws/aws-crt-php", + "version": "v1.0.4", "source": { "type": "git", - "url": "https://github.com/appstract/laravel-blade-directives.git", - "reference": "398d36a1c1f2740c81358b99473fd1564a81c406" + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "f5c64ee7c5fce196e2519b3d9b7138649efe032d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appstract/laravel-blade-directives/zipball/398d36a1c1f2740c81358b99473fd1564a81c406", - "reference": "398d36a1c1f2740c81358b99473fd1564a81c406", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/f5c64ee7c5fce196e2519b3d9b7138649efe032d", + "reference": "f5c64ee7c5fce196e2519b3d9b7138649efe032d", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^4.8.35|^5.6.3" }, "type": "library", - "extra": { - "laravel": { - "providers": [ - "Appstract\\BladeDirectives\\BladeDirectivesServiceProvider" - ] - } - }, "autoload": { - "psr-4": { - "Appstract\\BladeDirectives\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Gijs Jorissen", - "role": "Developer", - "email": "hello@appstract.team", - "homepage": "https://appstract.team" + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" } ], - "description": "Handy Blade directives", - "homepage": "https://github.com/appstract/laravel-blade-directives", + "description": "AWS Common Runtime for PHP", + "homepage": "http://aws.amazon.com/sdkforphp", "keywords": [ - "appstract", - "laravel-blade-directives" + "amazon", + "aws", + "crt", + "sdk" ], - "time": "2018-01-08T13:58:34+00:00" + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.0.4" + }, + "time": "2023-01-31T23:08:25+00:00" }, { "name": "aws/aws-sdk-php", - "version": "3.74.1", + "version": "3.260.1", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "e02575af8021ee57b818107c1fd8759110374044" + "reference": "964653acda337343e64344426fa609cf1df930e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e02575af8021ee57b818107c1fd8759110374044", - "reference": "e02575af8021ee57b818107c1fd8759110374044", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/964653acda337343e64344426fa609cf1df930e1", + "reference": "964653acda337343e64344426fa609cf1df930e1", "shasum": "" }, "require": { + "aws/aws-crt-php": "^1.0.4", "ext-json": "*", "ext-pcre": "*", "ext-simplexml": "*", - "ext-spl": "*", - "guzzlehttp/guzzle": "^5.3.3|^6.2.1", - "guzzlehttp/promises": "~1.0", - "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "~2.2", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/psr7": "^1.8.5 || ^2.3", + "mtdowling/jmespath.php": "^2.6", "php": ">=5.5" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", "aws/aws-php-sns-message-validator": "~1.0", "behat/behat": "~3.0", + "composer/composer": "^1.10.22", + "dms/phpunit-arraysubset-asserts": "^0.4.0", "doctrine/cache": "~1.4", "ext-dom": "*", "ext-openssl": "*", "ext-pcntl": "*", "ext-sockets": "*", "nette/neon": "^2.3", - "phpunit/phpunit": "^4.8.35|^5.4.3", - "psr/cache": "^1.0" + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3 || ^4.0", + "yoast/phpunit-polyfills": "^1.0" }, "suggest": { "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", @@ -111,12 +114,12 @@ } }, "autoload": { - "psr-4": { - "Aws\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "Aws\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -140,40 +143,103 @@ "s3", "sdk" ], - "time": "2018-11-21T19:18:43+00:00" + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.260.1" + }, + "time": "2023-02-22T19:23:27+00:00" }, { - "name": "cakephp/chronos", - "version": "1.2.3", + "name": "brick/math", + "version": "0.10.2", "source": { "type": "git", - "url": "https://github.com/cakephp/chronos.git", - "reference": "395110125ff577f080fa064dca5c5608a4e77ee1" + "url": "https://github.com/brick/math.git", + "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/395110125ff577f080fa064dca5c5608a4e77ee1", - "reference": "395110125ff577f080fa064dca5c5608a4e77ee1", + "url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f", + "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f", "shasum": "" }, "require": { - "php": "^5.5.9|^7" + "ext-json": "*", + "php": "^7.4 || ^8.0" }, "require-dev": { - "athletic/athletic": "~0.1", - "cakephp/cakephp-codesniffer": "^3.0", - "phpbench/phpbench": "@dev", - "phpstan/phpstan": "^0.6.4", - "phpunit/phpunit": "<6.0 || ^7.0" + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "4.25.0" }, "type": "library", "autoload": { "psr-4": { - "Cake\\Chronos\\": "src/" - }, - "files": [ - "src/carbon_compat.php" - ] + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.10.2" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2022-08-10T22:54:19+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "f41715465d65213d644d3141a6a93081be5d3549" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", + "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -181,93 +247,69 @@ ], "authors": [ { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "http://nesbot.com" + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" }, { - "name": "The CakePHP Team", - "homepage": "http://cakephp.org" + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" } ], - "description": "A simple API extension for DateTime.", - "homepage": "http://cakephp.org", + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", "keywords": [ - "date", - "datetime", - "time" + "access", + "data", + "dot", + "notation" ], - "time": "2018-10-18T22:02:21+00:00" - }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "0.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "@stable" - }, - "type": "project", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "time": "2014-10-24T07:27:01+00:00" + "time": "2022-10-27T11:44:00+00:00" }, { "name": "doctrine/cache", - "version": "v1.8.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", "shasum": "" }, "require": { - "php": "~7.1" + "php": "~7.1 || ^8.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^4.0", - "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0", - "predis/predis": "~1.0" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" @@ -278,6 +320,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -286,10 +332,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -299,42 +341,74 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "https://www.doctrine-project.org", + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", "keywords": [ + "abstraction", + "apcu", "cache", - "caching" + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" ], - "time": "2018-08-21T18:01:43+00:00" + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" }, { "name": "doctrine/dbal", - "version": "v2.8.0", + "version": "3.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "5140a64c08b4b607b9bedaae0cedd26f04a0e621" + "reference": "85b98cb23c8af471a67abfe14485da696bcabc2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/5140a64c08b4b607b9bedaae0cedd26f04a0e621", - "reference": "5140a64c08b4b607b9bedaae0cedd26f04a0e621", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/85b98cb23c8af471a67abfe14485da696bcabc2e", + "reference": "85b98cb23c8af471a67abfe14485da696bcabc2e", "shasum": "" }, "require": { - "doctrine/cache": "^1.0", - "doctrine/event-manager": "^1.0", - "ext-pdo": "*", - "php": "^7.1" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "^4.0", - "jetbrains/phpstorm-stubs": "^2018.1.2", - "phpstan/phpstan": "^0.10.1", - "phpunit/phpunit": "^7.1.2", - "phpunit/phpunit-mock-objects": "!=3.2.4,!=3.2.5", - "symfony/console": "^2.0.5|^3.0|^4.0", - "symfony/phpunit-bridge": "^3.4.5|^4.0.5" + "doctrine/coding-standard": "11.1.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2022.3", + "phpstan/phpstan": "1.9.14", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "9.6.3", + "psalm/plugin-phpunit": "0.18.4", + "squizlabs/php_codesniffer": "3.7.1", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.30.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -343,15 +417,9 @@ "bin/doctrine-dbal" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "3.0.x-dev" - } - }, "autoload": { - "psr-0": { - "Doctrine\\DBAL\\": "lib/" + "psr-4": { + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -359,6 +427,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -367,58 +439,126 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" } ], - "description": "Database Abstraction Layer", - "homepage": "http://www.doctrine-project.org", + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", "keywords": [ + "abstraction", "database", + "db2", "dbal", - "persistence", - "queryobject" + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" ], - "time": "2018-07-13T03:16:35+00:00" + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.6.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2023-02-07T22:52:03+00:00" }, { - "name": "doctrine/event-manager", + "name": "doctrine/deprecations", "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", - "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", "shasum": "" }, "require": { - "php": "^7.1" - }, - "conflict": { - "doctrine/common": "<2.9@dev" + "php": "^7.1|^8.0" }, "require-dev": { - "doctrine/coding-standard": "^4.0", - "phpunit/phpunit": "^7.0" + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -426,6 +566,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -434,10 +578,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -451,44 +591,64 @@ "email": "ocramius@gmail.com" } ], - "description": "Doctrine Event Manager component", + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", "homepage": "https://www.doctrine-project.org/projects/event-manager.html", "keywords": [ "event", - "eventdispatcher", - "eventmanager" + "event dispatcher", + "event manager", + "event system", + "events" ], - "time": "2018-06-11T11:59:03+00:00" + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2022-10-12T20:59:15+00:00" }, { "name": "doctrine/inflector", - "version": "v1.3.0", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" } }, "notification-url": "https://packagist.org/downloads/", @@ -496,6 +656,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -504,10 +668,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -517,42 +677,68 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", "keywords": [ "inflection", - "pluralize", - "singularize", - "string" + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" ], - "time": "2018-01-09T20:05:19+00:00" + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.6" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2022-10-20T09:10:12+00:00" }, { "name": "doctrine/lexer", - "version": "v1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -560,46 +746,74 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ + "annotations", + "docblock", "lexer", - "parser" + "parser", + "php" ], - "time": "2014-09-09T13:34:57+00:00" + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-12-15T16:57:16+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v2.2.0", + "version": "v3.3.2", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5" + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/92a2c3768d50e21a1f26a53cb795ce72806266c5", - "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpunit/phpunit": "~6.4" + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", "autoload": { @@ -612,11 +826,6 @@ "MIT" ], "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, { "name": "Chris Tankersley", "email": "chris@ctankersley.com", @@ -628,30 +837,40 @@ "cron", "schedule" ], - "time": "2018-06-06T03:12:17+00:00" + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" }, { "name": "egulias/email-validator", - "version": "2.1.6", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "0578b32b30b22de3e8664f797cf846fc9246f786" + "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0578b32b30b22de3e8664f797cf846fc9246f786", - "reference": "0578b32b30b22de3e8664f797cf846fc9246f786", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", + "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff", "shasum": "" }, "require": { - "doctrine/lexer": "^1.0.1", - "php": ">= 5.5" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "dominicsayers/isemail": "dev-master", - "phpunit/phpunit": "^4.8.35||^5.7||^6.0", - "satooshi/php-coveralls": "^1.0.1" + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^4.30" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -659,12 +878,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { "psr-4": { - "Egulias\\EmailValidator\\": "EmailValidator" + "Egulias\\EmailValidator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -685,139 +904,196 @@ "validation", "validator" ], - "time": "2018-09-25T20:47:26+00:00" - }, - { - "name": "erusev/parsedown", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/erusev/parsedown.git", - "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1" + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.1" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", - "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35" - }, - "type": "library", - "autoload": { - "psr-0": { - "Parsedown": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Emanuil Rusev", - "email": "hello@erusev.com", - "homepage": "http://erusev.com" + "url": "https://github.com/egulias", + "type": "github" } ], - "description": "Parser for Markdown.", - "homepage": "http://parsedown.org", - "keywords": [ - "markdown", - "parser" - ], - "time": "2018-03-08T01:11:30+00:00" + "time": "2023-01-14T14:17:03+00:00" }, { - "name": "fideloper/proxy", - "version": "4.0.0", + "name": "fruitcake/php-cors", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/fideloper/TrustedProxy.git", - "reference": "cf8a0ca4b85659b9557e206c90110a6a4dba980a" + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/cf8a0ca4b85659b9557e206c90110a6a4dba980a", - "reference": "cf8a0ca4b85659b9557e206c90110a6a4dba980a", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", + "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e", "shasum": "" }, "require": { - "illuminate/contracts": "~5.0", - "php": ">=5.4.0" + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6" }, "require-dev": { - "illuminate/http": "~5.6", - "mockery/mockery": "~1.0", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Fideloper\\Proxy\\TrustedProxyServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Fideloper\\Proxy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Fidao", - "email": "fideloper@gmail.com" - } - ], - "description": "Set trusted proxies for Laravel", - "keywords": [ - "load balancing", - "proxy", - "trusted proxy" - ], - "time": "2018-02-07T20:20:57+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "shasum": "" - }, - "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-main": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2022-02-20T15:07:15+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8", + "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.28 || ^9.5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2022-07-30T15:56:11+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.9 || ^2.4", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "7.5-dev" } }, "autoload": { @@ -833,160 +1109,381 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", + "psr-18", + "psr-7", "rest", "web service" ], - "time": "2018-04-22T15:46:56+00:00" + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-08-28T15:39:27+00:00" }, { "name": "guzzlehttp/promises", - "version": "v1.3.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + "reference": "b94b2807d85443f9719887892882d0329d1e2598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", + "reference": "b94b2807d85443f9719887892882d0329d1e2598", "shasum": "" }, "require": { - "php": ">=5.5.0" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^4.0" + "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2022-08-28T14:55:35+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.4.2", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + "reference": "67c26b443f348a51926030c83481b85718457d3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", + "reference": "67c26b443f348a51926030c83481b85718457d3d", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", + "psr-7", "request", "response", "stream", "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.4.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-10-26T14:07:24+00:00" }, { - "name": "hashids/hashids", - "version": "3.0.0", + "name": "guzzlehttp/uri-template", + "version": "v1.0.1", "source": { "type": "git", - "url": "https://github.com/vinkla/hashids.git", - "reference": "b6c61142bfe36d43740a5419d11c351dddac0458" + "url": "https://github.com/guzzle/uri-template.git", + "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vinkla/hashids/zipball/b6c61142bfe36d43740a5419d11c351dddac0458", - "reference": "b6c61142bfe36d43740a5419d11c351dddac0458", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2", + "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.17" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.5.19 || ^9.5.8", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2021-10-07T12:57:01+00:00" + }, + { + "name": "hashids/hashids", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/vinkla/hashids.git", + "reference": "197171016b77ddf14e259e186559152eb3f8cf33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vinkla/hashids/zipball/197171016b77ddf14e259e186559152eb3f8cf33", + "reference": "197171016b77ddf14e259e186559152eb3f8cf33", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-bcmath": "Required to use BC Math arbitrary precision mathematics (*).", @@ -995,7 +1492,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1010,17 +1507,15 @@ "authors": [ { "name": "Ivan Akimov", - "email": "ivan@barreleye.com", - "homepage": "https://twitter.com/IvanAkimov" + "email": "ivan@barreleye.com" }, { "name": "Vincent Klaiber", - "email": "hello@vinkla.com", - "homepage": "https://vinkla.com" + "email": "hello@doubledip.se" } ], "description": "Generate short, unique, non-sequential ids (like YouTube and Bitly) from numbers", - "homepage": "http://hashids.org/php", + "homepage": "https://hashids.org/php", "keywords": [ "bitly", "decode", @@ -1032,179 +1527,32 @@ "obfuscate", "youtube" ], - "time": "2018-03-12T16:30:09+00:00" - }, - { - "name": "igaster/laravel-theme", - "version": "v2.0.9", - "source": { - "type": "git", - "url": "https://github.com/igaster/laravel-theme.git", - "reference": "c0b93dfcfac3602d6d224ad79f76795f6918f4c1" + "support": { + "issues": "https://github.com/vinkla/hashids/issues", + "source": "https://github.com/vinkla/hashids/tree/5.0.2" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igaster/laravel-theme/zipball/c0b93dfcfac3602d6d224ad79f76795f6918f4c1", - "reference": "c0b93dfcfac3602d6d224ad79f76795f6918f4c1", - "shasum": "" - }, - "require": { - "illuminate/contracts": "5.4.*|5.5.*|5.6.*|5.7.*" - }, - "require-dev": { - "orchestra/testbench": "~3.4", - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "orchestra/asset": "Use '@css' and '@js' in Blade files" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Igaster\\LaravelTheme\\themeServiceProvider" - ], - "aliases": { - "Theme": "Igaster\\LaravelTheme\\Facades\\Theme" - } - } - }, - "autoload": { - "psr-4": { - "Igaster\\LaravelTheme\\": "src/", - "Igaster\\LaravelTheme\\Tests\\": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Giannis Gasteratos", - "email": "igasteratos@gmail.com" - } - ], - "description": "Laravel 5 Themes: Asset & Views folder per theme. Theme inheritance. Blade integration and more...", - "homepage": "https://github.com/Igaster/laravel-theme.git", - "keywords": [ - "assets", - "blade", - "laravel-5", - "package", - "themes", - "views" - ], - "time": "2018-09-17T07:57:42+00:00" - }, - { - "name": "jakub-onderka/php-console-color", - "version": "v0.2", - "source": { - "type": "git", - "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", - "reference": "d5deaecff52a0d61ccb613bb3804088da0307191" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/d5deaecff52a0d61ccb613bb3804088da0307191", - "reference": "d5deaecff52a0d61ccb613bb3804088da0307191", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "jakub-onderka/php-code-style": "1.0", - "jakub-onderka/php-parallel-lint": "1.0", - "jakub-onderka/php-var-dump-check": "0.*", - "phpunit/phpunit": "~4.3", - "squizlabs/php_codesniffer": "1.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "JakubOnderka\\PhpConsoleColor\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Jakub Onderka", - "email": "jakub.onderka@gmail.com" - } - ], - "time": "2018-09-29T17:23:10+00:00" - }, - { - "name": "jakub-onderka/php-console-highlighter", - "version": "v0.4", - "source": { - "type": "git", - "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", - "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/9f7a229a69d52506914b4bc61bfdb199d90c5547", - "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "jakub-onderka/php-console-color": "~0.2", - "php": ">=5.4.0" - }, - "require-dev": { - "jakub-onderka/php-code-style": "~1.0", - "jakub-onderka/php-parallel-lint": "~1.0", - "jakub-onderka/php-var-dump-check": "~0.1", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "JakubOnderka\\PhpConsoleHighlighter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jakub Onderka", - "email": "acci@acci.cz", - "homepage": "http://www.acci.cz/" - } - ], - "description": "Highlight PHP code in terminal", - "time": "2018-09-29T18:48:56+00:00" + "time": "2023-02-23T15:00:54+00:00" }, { "name": "laracasts/utilities", - "version": "3.0", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer.git", - "reference": "298fb3c6f29901a4550c4f98b57c05f368341d04" + "reference": "1cc5d5b9670e5be392df38e0b648fa4842c5a1f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laracasts/PHP-Vars-To-Js-Transformer/zipball/298fb3c6f29901a4550c4f98b57c05f368341d04", - "reference": "298fb3c6f29901a4550c4f98b57c05f368341d04", + "url": "https://api.github.com/repos/laracasts/PHP-Vars-To-Js-Transformer/zipball/1cc5d5b9670e5be392df38e0b648fa4842c5a1f8", + "reference": "1cc5d5b9670e5be392df38e0b648fa4842c5a1f8", "shasum": "" }, "require": { - "illuminate/support": "~5.0", - "php": ">=5.4.0" + "illuminate/support": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "php": ">=5.5.0|>=7.2.5|>=8.0.0" }, "require-dev": { - "phpspec/phpspec": "~2.0" + "phpspec/phpspec": ">=2.0" }, "type": "library", "extra": { @@ -1240,56 +1588,81 @@ "javascript", "laravel" ], - "time": "2017-09-01T17:25:57+00:00" + "support": { + "issues": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer/issues", + "source": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer/tree/3.2.2" + }, + "time": "2023-02-03T13:22:09+00:00" }, { "name": "laravel/framework", - "version": "v5.7.14", + "version": "v10.1.4", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "ad6c1fe1e455c0f73a431928282704879ccbd856" + "reference": "da95415a83fe6216dc78efebfc311365cf4cade0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/ad6c1fe1e455c0f73a431928282704879ccbd856", - "reference": "ad6c1fe1e455c0f73a431928282704879ccbd856", + "url": "https://api.github.com/repos/laravel/framework/zipball/da95415a83fe6216dc78efebfc311365cf4cade0", + "reference": "da95415a83fe6216dc78efebfc311365cf4cade0", "shasum": "" }, "require": { - "doctrine/inflector": "^1.1", - "dragonmantank/cron-expression": "^2.0", - "erusev/parsedown": "^1.7", + "brick/math": "^0.9.3|^0.10.2|^0.11", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", "ext-mbstring": "*", "ext-openssl": "*", - "league/flysystem": "^1.0.8", - "monolog/monolog": "^1.12", - "nesbot/carbon": "^1.26.3", - "opis/closure": "^3.1", - "php": "^7.1.3", - "psr/container": "^1.0", - "psr/simple-cache": "^1.0", - "ramsey/uuid": "^3.7", - "swiftmailer/swiftmailer": "^6.0", - "symfony/console": "^4.1", - "symfony/debug": "^4.1", - "symfony/finder": "^4.1", - "symfony/http-foundation": "^4.1", - "symfony/http-kernel": "^4.1", - "symfony/process": "^4.1", - "symfony/routing": "^4.1", - "symfony/var-dumper": "^4.1", - "tijsverkoyen/css-to-inline-styles": "^2.2.1", - "vlucas/phpdotenv": "^2.2" + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.62.1", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.2", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.4.1", + "voku/portable-ascii": "^2.0" }, "conflict": { "tightenco/collect": "<5.5.33" }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, "replace": { "illuminate/auth": "self.version", "illuminate/broadcasting": "self.version", "illuminate/bus": "self.version", "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", "illuminate/container": "self.version", @@ -1302,70 +1675,101 @@ "illuminate/hashing": "self.version", "illuminate/http": "self.version", "illuminate/log": "self.version", + "illuminate/macroable": "self.version", "illuminate/mail": "self.version", "illuminate/notifications": "self.version", "illuminate/pagination": "self.version", "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", "illuminate/routing": "self.version", "illuminate/session": "self.version", "illuminate/support": "self.version", + "illuminate/testing": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", "illuminate/view": "self.version" }, "require-dev": { - "aws/aws-sdk-php": "^3.0", - "doctrine/dbal": "^2.6", - "filp/whoops": "^2.1.4", - "guzzlehttp/guzzle": "^6.3", - "league/flysystem-cached-adapter": "^1.0", - "mockery/mockery": "^1.0", - "moontoast/math": "^1.1", - "orchestra/testbench-core": "3.7.*", - "pda/pheanstalk": "^3.0", - "phpunit/phpunit": "^7.0", - "predis/predis": "^1.1.1", - "symfony/css-selector": "^4.1", - "symfony/dom-crawler": "^4.1", - "true/punycode": "^2.1" + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "orchestra/testbench-core": "^8.0", + "pda/pheanstalk": "^4.0", + "phpstan/phpdoc-parser": "^1.15", + "phpstan/phpstan": "^1.4.7", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4" }, "suggest": { - "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (^3.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).", - "ext-pcntl": "Required to use all features of the queue worker.", + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", - "filp/whoops": "Required for friendly error pages in development (^2.1.4).", - "fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).", - "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (^6.0).", - "laravel/tinker": "Required to use the tinker console command (^1.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", - "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", - "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (^1.0).", - "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", - "moontoast/math": "Required to use ordered UUIDs (^1.1).", - "nexmo/client": "Required to use the Nexmo transport (^1.0).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^3.0).", - "predis/predis": "Required to use the redis cache and queue drivers (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).", - "symfony/css-selector": "Required to use some of the crawler integration testing tools (^4.1).", - "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (^4.1).", - "symfony/psr-http-message-bridge": "Required to psr7 bridging features (^1.0)." + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.7-dev" + "dev-master": "10.x-dev" } }, "autoload": { "files": [ + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" ], "psr-4": { - "Illuminate\\": "src/Illuminate/" + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -1384,40 +1788,226 @@ "framework", "laravel" ], - "time": "2018-11-21T13:46:08+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-02-23T12:25:38+00:00" }, { - "name": "laravel/tinker", - "version": "v1.0.8", + "name": "laravel/helpers", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/laravel/tinker.git", - "reference": "cafbf598a90acde68985660e79b2b03c5609a405" + "url": "https://github.com/laravel/helpers.git", + "reference": "4dd0f9436d3911611622a6ced8329a1710576f60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/cafbf598a90acde68985660e79b2b03c5609a405", - "reference": "cafbf598a90acde68985660e79b2b03c5609a405", + "url": "https://api.github.com/repos/laravel/helpers/zipball/4dd0f9436d3911611622a6ced8329a1710576f60", + "reference": "4dd0f9436d3911611622a6ced8329a1710576f60", "shasum": "" }, "require": { - "illuminate/console": "~5.1", - "illuminate/contracts": "~5.1", - "illuminate/support": "~5.1", - "php": ">=5.5.9", - "psy/psysh": "0.7.*|0.8.*|0.9.*", - "symfony/var-dumper": "~3.0|~4.0" + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "php": "^7.1.3|^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.0|~5.0" - }, - "suggest": { - "illuminate/database": "The Illuminate Database package (~5.1)." + "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Dries Vints", + "email": "dries@laravel.com" + } + ], + "description": "Provides backwards compatibility for helpers in the latest Laravel release.", + "keywords": [ + "helpers", + "laravel" + ], + "support": { + "source": "https://github.com/laravel/helpers/tree/v1.6.0" + }, + "time": "2023-01-09T14:48:11+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "d09d69bac55708fcd4a3b305d760e673d888baf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/d09d69bac55708fcd4a3b305d760e673d888baf9", + "reference": "d09d69bac55708fcd4a3b305d760e673d888baf9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.21|^10.0", + "illuminate/contracts": "^9.21|^10.0", + "illuminate/database": "^9.21|^10.0", + "illuminate/support": "^9.21|^10.0", + "php": "^8.0.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2023-01-13T15:41:49+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "nesbot/carbon": "^2.61", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2023-01-30T18:31:20+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.8.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", + "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.10.4|^0.11.1", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" }, "laravel": { "providers": [ @@ -1447,58 +2037,445 @@ "laravel", "psysh" ], - "time": "2018-10-12T19:39:35+00:00" + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.8.1" + }, + "time": "2023-02-15T16:40:09+00:00" }, { - "name": "league/flysystem", - "version": "1.0.49", + "name": "laravel/ui", + "version": "v4.2.1", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "a63cc83d8a931b271be45148fa39ba7156782ffd" + "url": "https://github.com/laravel/ui.git", + "reference": "05ff7ac1eb55e2dfd10edcfb18c953684d693907" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a63cc83d8a931b271be45148fa39ba7156782ffd", - "reference": "a63cc83d8a931b271be45148fa39ba7156782ffd", + "url": "https://api.github.com/repos/laravel/ui/zipball/05ff7ac1eb55e2dfd10edcfb18c953684d693907", + "reference": "05ff7ac1eb55e2dfd10edcfb18c953684d693907", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "php": ">=5.5.9" - }, - "conflict": { - "league/flysystem-sftp": "<1.0.6" + "illuminate/console": "^9.21|^10.0", + "illuminate/filesystem": "^9.21|^10.0", + "illuminate/support": "^9.21|^10.0", + "illuminate/validation": "^9.21|^10.0", + "php": "^8.0" }, "require-dev": { - "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.10" - }, - "suggest": { - "ext-fileinfo": "Required for MimeType", - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "4.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Ui\\UiServiceProvider" + ] } }, "autoload": { "psr-4": { - "League\\Flysystem\\": "src/" + "Laravel\\Ui\\": "src/", + "Illuminate\\Foundation\\Auth\\": "auth-backend/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel UI utilities and presets.", + "keywords": [ + "laravel", + "ui" + ], + "support": { + "source": "https://github.com/laravel/ui/tree/v4.2.1" + }, + "time": "2023-02-17T09:17:24+00:00" + }, + { + "name": "lcobucci/clock", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/039ef98c6b57b101d10bd11d8fdfda12cbd996dc", + "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.26", + "lcobucci/coding-standard": "^9.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.27" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2022-12-19T15:00:24+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-sodium": "*", + "lcobucci/clock": "^2.0 || ^3.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "infection/infection": "^0.21", + "lcobucci/coding-standard": "^6.0", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/php-invoker": "^3.1", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/4.3.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-01-02T13:28:00+00:00" + }, + { + "name": "league/commonmark", + "version": "2.3.9", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c1e114f74e518daca2729ea8c4bf1167038fa4b5", + "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.30.0", + "commonmark/commonmark.js": "0.30.0", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2023-02-15T14:07:24+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.12.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "81e87e74dd5213795c7846d65089712d2dda90ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/81e87e74dd5213795c7846d65089712d2dda90ce", + "reference": "81e87e74dd5213795c7846d65089712d2dda90ce", + "shasum": "" + }, + "require": { + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5", + "async-aws/simple-s3": "^1.1", + "aws/aws-sdk-php": "^3.220.0", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "microsoft/azure-storage-blob": "^1.1", + "phpseclib/phpseclib": "^3.0.14", + "phpstan/phpstan": "^0.12.26", + "phpunit/phpunit": "^9.5.11", + "sabre/dav": "^4.3.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1508,55 +2485,202 @@ "authors": [ { "name": "Frank de Jonge", - "email": "info@frenky.net" + "email": "info@frankdejonge.nl" } ], - "description": "Filesystem abstraction: Many filesystems, one API.", + "description": "File storage abstraction for PHP", "keywords": [ - "Cloud Files", "WebDAV", - "abstraction", "aws", "cloud", - "copy.com", - "dropbox", - "file systems", + "file", "files", "filesystem", "filesystems", "ftp", - "rackspace", - "remote", "s3", "sftp", "storage" ], - "time": "2018-11-23T23:41:29+00:00" + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.12.3" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2023-02-18T15:32:41+00:00" }, { - "name": "league/fractal", - "version": "0.17.0", + "name": "league/flysystem-aws-s3-v3", + "version": "3.12.2", "source": { "type": "git", - "url": "https://github.com/thephpleague/fractal.git", - "reference": "a0b350824f22fc2fdde2500ce9d6851a3f275b0e" + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "645e14e4a80bd2da8b01e57388e7296a695a80c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/fractal/zipball/a0b350824f22fc2fdde2500ce9d6851a3f275b0e", - "reference": "a0b350824f22fc2fdde2500ce9d6851a3f275b0e", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/645e14e4a80bd2da8b01e57388e7296a695a80c2", + "reference": "645e14e4a80bd2da8b01e57388e7296a695a80c2", "shasum": "" }, "require": { - "php": ">=5.4" + "aws/aws-sdk-php": "^3.220.0", + "league/flysystem": "^3.10.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3V3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "AWS S3 filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "aws", + "file", + "files", + "filesystem", + "s3", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues", + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.12.2" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2023-01-17T14:15:08+00:00" + }, + { + "name": "league/flysystem-memory", + "version": "3.10.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-memory.git", + "reference": "5405162ac81f4de5aa5fa01aae7d07382b7c797b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-memory/zipball/5405162ac81f4de5aa5fa01aae7d07382b7c797b", + "reference": "5405162ac81f4de5aa5fa01aae7d07382b7c797b", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^2.0.0 || ^3.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\InMemory\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "In-memory filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "memory" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-memory/issues", + "source": "https://github.com/thephpleague/flysystem-memory/tree/3.10.3" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-10-26T18:30:26+00:00" + }, + { + "name": "league/fractal", + "version": "0.20.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/fractal.git", + "reference": "8b9d39b67624db9195c06f9c1ffd0355151eaf62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/fractal/zipball/8b9d39b67624db9195c06f9c1ffd0355151eaf62", + "reference": "8b9d39b67624db9195c06f9c1ffd0355151eaf62", + "shasum": "" + }, + "require": { + "php": ">=7.4" }, "require-dev": { "doctrine/orm": "^2.5", "illuminate/contracts": "~5.0", - "mockery/mockery": "~0.9", + "mockery/mockery": "^1.3", "pagerfanta/pagerfanta": "~1.0.0", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.5", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "~3.4", + "vimeo/psalm": "^4.22", "zendframework/zend-paginator": "~2.3" }, "suggest": { @@ -1567,7 +2691,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.13-dev" + "dev-master": "0.20.x-dev" } }, "autoload": { @@ -1595,38 +2719,39 @@ "league", "rest" ], - "time": "2017-06-12T11:04:56+00:00" + "support": { + "issues": "https://github.com/thephpleague/fractal/issues", + "source": "https://github.com/thephpleague/fractal/tree/0.20.1" + }, + "time": "2022-04-11T12:47:17+00:00" }, { - "name": "lord/laroute", - "version": "2.4.8", + "name": "league/mime-type-detection", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/aaronlord/laroute.git", - "reference": "7242310f02ebc3f3d6a94b00db95ad0978b1802b" + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aaronlord/laroute/zipball/7242310f02ebc3f3d6a94b00db95ad0978b1802b", - "reference": "7242310f02ebc3f3d6a94b00db95ad0978b1802b", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", "shasum": "" }, "require": { - "illuminate/config": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "illuminate/console": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "illuminate/filesystem": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "illuminate/routing": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "php": ">=5.4.0" + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" }, "require-dev": { - "mockery/mockery": "dev-master", - "phpunit/phpunit": "~4.0" + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" }, "type": "library", "autoload": { "psr-4": { - "Lord\\Laroute\\": "src/" + "League\\MimeTypeDetection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1635,18 +2760,26 @@ ], "authors": [ { - "name": "Aaron Lord", - "email": "hello@aaronlord.is" + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" } ], - "description": "Access Laravels URL/Route helper functions, from JavaScript.", - "keywords": [ - "javascript", - "laravel", - "routes", - "routing" + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } ], - "time": "2018-09-11T00:19:41+00:00" + "time": "2022-04-17T13:12:02+00:00" }, { "name": "matriphe/iso-639", @@ -1690,59 +2823,72 @@ "language", "laravel" ], + "support": { + "issues": "https://github.com/matriphe/php-iso-639/issues", + "source": "https://github.com/matriphe/php-iso-639/tree/master" + }, "time": "2017-07-19T15:11:19+00:00" }, { "name": "monolog/monolog", - "version": "1.24.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" + "reference": "9b5daeaffce5b926cac47923798bba91059e60e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/9b5daeaffce5b926cac47923798bba91059e60e2", + "reference": "9b5daeaffce5b926cac47923798bba91059e60e2", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0" + "psr/log-implementation": "3.0.0" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "aws/aws-sdk-php": "^3.0", "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^9.5.26", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -1758,37 +2904,53 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "homepage": "https://github.com/Seldaek/monolog", "keywords": [ "log", "logging", "psr-3" ], - "time": "2018-11-05T09:00:11+00:00" + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.3.1" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2023-02-06T13:46:10+00:00" }, { "name": "mtdowling/jmespath.php", - "version": "2.4.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac" + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/adcc9531682cf87dfda21e1fd5d0e7a41d292fac", - "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": "^5.4 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "composer/xdebug-handler": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^7.5.15" }, "bin": [ "bin/jp.php" @@ -1796,16 +2958,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.6-dev" } }, "autoload": { - "psr-4": { - "JmesPath\\": "src/" - }, "files": [ "src/JmesPath.php" - ] + ], + "psr-4": { + "JmesPath\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1823,44 +2985,69 @@ "json", "jsonpath" ], - "time": "2016-12-03T22:08:25+00:00" + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1" + }, + "time": "2021-06-14T00:11:39+00:00" }, { "name": "nesbot/carbon", - "version": "1.36.1", + "version": "2.66.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "63da8cdf89d7a5efe43aabc794365f6e7b7b8983" + "reference": "496712849902241f04902033b0441b269effe001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/63da8cdf89d7a5efe43aabc794365f6e7b7b8983", - "reference": "63da8cdf89d7a5efe43aabc794365f6e7b7b8983", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", + "reference": "496712849902241f04902033b0441b269effe001", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/translation": "~2.6 || ~3.0 || ~4.0" + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7" - }, - "suggest": { - "friendsofphp/php-cs-fixer": "Needed for the `composer phpcs` command. Allow to automatically fix code style.", - "phpstan/phpstan": "Needed for the `composer phpstan` command. Allow to detect potential errors." + "doctrine/dbal": "^2.0 || ^3.1.4", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" }, + "bin": [ + "bin/carbon" + ], "type": "library", "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { "psr-4": { - "": "src/" + "Carbon\\": "src/Carbon/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1871,30 +3058,202 @@ { "name": "Brian Nesbitt", "email": "brian@nesbot.com", - "homepage": "http://nesbot.com" + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" } ], - "description": "A simple API extension for DateTime.", - "homepage": "http://carbon.nesbot.com", + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", "keywords": [ "date", "datetime", "time" ], - "time": "2018-11-22T18:23:02+00:00" + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2023-01-29T18:53:47+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.1.0", + "name": "nette/schema", + "version": "v1.2.3", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "d0230c5c77a7e3cfa69446febf340978540958c0" + "url": "https://github.com/nette/schema.git", + "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/d0230c5c77a7e3cfa69446febf340978540958c0", - "reference": "d0230c5c77a7e3cfa69446febf340978540958c0", + "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "shasum": "" + }, + "require": { + "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", + "php": ">=7.1 <8.3" + }, + "require-dev": { + "nette/tester": "^2.3 || ^2.4", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.2.3" + }, + "time": "2022-10-13T01:24:26+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", + "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "shasum": "" + }, + "require": { + "php": ">=8.0 <8.3" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.4", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.0" + }, + "time": "2023-02-02T10:41:53+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.3", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { @@ -1902,7 +3261,8 @@ "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.0" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -1910,7 +3270,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.9-dev" } }, "autoload": { @@ -1932,42 +3292,58 @@ "parser", "php" ], - "time": "2018-10-10T09:24:14+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + }, + "time": "2023-01-16T22:05:37+00:00" }, { - "name": "opis/closure", - "version": "3.1.1", + "name": "nunomaduro/termwind", + "version": "v1.15.1", "source": { "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "d3209e46ad6c69a969b705df0738fd0dbe26ef9e" + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/d3209e46ad6c69a969b705df0738fd0dbe26ef9e", - "reference": "d3209e46ad6c69a969b705df0738fd0dbe26ef9e", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", "shasum": "" }, "require": { - "php": "^5.4 || ^7.0" + "ext-mbstring": "*", + "php": "^8.0", + "symfony/console": "^5.3.0|^6.0.0" }, "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0" + "ergebnis/phpstan-rules": "^1.0.", + "illuminate/console": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "laravel/pint": "^1.0.0", + "pestphp/pest": "^1.21.0", + "pestphp/pest-plugin-mock": "^1.0", + "phpstan/phpstan": "^1.4.6", + "phpstan/phpstan-strict-rules": "^1.1.0", + "symfony/var-dumper": "^5.2.7|^6.0.0", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] } }, "autoload": { - "psr-4": { - "Opis\\Closure\\": "src/" - }, "files": [ - "functions.php" - ] + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1975,46 +3351,59 @@ ], "authors": [ { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" } ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", + "description": "Its like Tailwind CSS, but for the console.", "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" + "cli", + "console", + "css", + "package", + "php", + "style" ], - "time": "2018-10-02T13:36:53+00:00" + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2023-02-08T01:06:31+00:00" }, { "name": "paragonie/constant_time_encoding", - "version": "v2.2.2", + "version": "v2.6.3", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33" + "reference": "58c3f47f650c94ec05a151692652a868995d2938" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/eccf915f45f911bfb189d1d1638d940ec6ee6e33", - "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", "shasum": "" }, "require": { - "php": "^7" + "php": "^7|^8" }, "require-dev": { - "phpunit/phpunit": "^6|^7", - "vimeo/psalm": "^1" + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" }, "type": "library", "autoload": { @@ -2055,37 +3444,38 @@ "hex2bin", "rfc4648" ], - "time": "2018-03-10T19:47:49+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" }, { "name": "paragonie/random_compat", - "version": "v2.0.17", + "version": "v9.99.100", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { - "php": ">=5.2.0" + "php": ">= 7" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*" + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -2104,42 +3494,224 @@ "pseudorandom", "random" ], - "time": "2018-07-04T16:31:37+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" }, { - "name": "pragmarx/google2fa", - "version": "v5.0.0", + "name": "phpoption/phpoption", + "version": "1.9.0", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "17c969c82f427dd916afe4be50bafc6299aef1b4" + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/17c969c82f427dd916afe4be50bafc6299aef1b4", - "reference": "17c969c82f427dd916afe4be50bafc6299aef1b4", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", + "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "~1.0|~2.0", - "paragonie/random_compat": ">=1", - "php": ">=5.4", - "symfony/polyfill-php56": "~1.2" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5|~6" + "bamarni/composer-bin-plugin": "^1.8", + "phpunit/phpunit": "^8.5.28 || ^9.5.21" }, "type": "library", "extra": { - "component": "package", + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.9-dev" } }, "autoload": { "psr-4": { - "PragmaRX\\Google2FA\\": "src/", - "PragmaRX\\Google2FA\\Tests\\": "tests/" + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2022-07-30T15:51:26+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.18", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "f28693d38ba21bb0d9f0c411ee5dae2b178201da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f28693d38ba21bb0d9f0c411ee5dae2b178201da", + "reference": "f28693d38ba21bb0d9f0c411ee5dae2b178201da", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.18" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2022-12-17T18:26:50+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.1", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.18", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2149,8 +3721,8 @@ "authors": [ { "name": "Antonio Carlos Ribeiro", - "role": "Creator & Designer", - "email": "acr@antoniocarlosribeiro.com" + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" } ], "description": "A One Time Password Authentication package, compatible with Google Authenticator.", @@ -2160,33 +3732,41 @@ "Two Factor Authentication", "google2fa" ], - "time": "2019-03-19T22:44:16+00:00" + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" + }, + "time": "2022-06-13T21:57:56+00:00" }, { "name": "predis/predis", - "version": "v1.1.1", + "version": "v2.1.1", "source": { "type": "git", - "url": "https://github.com/nrk/predis.git", - "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1" + "url": "https://github.com/predis/predis.git", + "reference": "c5b60884e89630f9518a7919f0566db438f0fc9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1", - "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1", + "url": "https://api.github.com/repos/predis/predis/zipball/c5b60884e89630f9518a7919f0566db438f0fc9a", + "reference": "c5b60884e89630f9518a7919f0566db438f0fc9a", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^8.0 || ~9.4.4" }, "suggest": { - "ext-curl": "Allows access to Webdis when paired with phpiredis", - "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + "ext-curl": "Allows access to Webdis when paired with phpiredis" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, "autoload": { "psr-4": { "Predis\\": "src/" @@ -2197,44 +3777,59 @@ "MIT" ], "authors": [ + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + }, { "name": "Daniele Alessandri", "email": "suppakilla@gmail.com", - "homepage": "http://clorophilla.net" + "homepage": "http://clorophilla.net", + "role": "Creator" } ], - "description": "Flexible and feature-complete Redis client for PHP and HHVM", - "homepage": "http://github.com/nrk/predis", + "description": "A flexible and feature-complete Redis client for PHP.", + "homepage": "http://github.com/predis/predis", "keywords": [ "nosql", "predis", "redis" ], - "time": "2016-06-16T16:22:20+00:00" + "support": { + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v2.1.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2023-01-17T20:57:35+00:00" }, { "name": "prologue/alerts", - "version": "0.4.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/prologuephp/alerts.git", - "reference": "d3bf5d7ea480cbbf372bb7f80e23e193ce4862c7" + "reference": "33e86d1f64dae7a8d6e29e7d6c282abc77a89b97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/prologuephp/alerts/zipball/d3bf5d7ea480cbbf372bb7f80e23e193ce4862c7", - "reference": "d3bf5d7ea480cbbf372bb7f80e23e193ce4862c7", + "url": "https://api.github.com/repos/prologuephp/alerts/zipball/33e86d1f64dae7a8d6e29e7d6c282abc77a89b97", + "reference": "33e86d1f64dae7a8d6e29e7d6c282abc77a89b97", "shasum": "" }, "require": { - "illuminate/config": "~5", - "illuminate/session": "~5", - "illuminate/support": "~5", - "php": ">=5.4.0" + "illuminate/config": "~9|^10", + "illuminate/session": "~9|^10", + "illuminate/support": "~9|^10" }, "require-dev": { - "mockery/mockery": "~0.9", - "phpunit/phpunit": "~4.1" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { @@ -2259,9 +3854,15 @@ "authors": [ { "name": "Dries Vints", - "role": "Maintainer", "email": "dries.vints@gmail.com", - "homepage": "http://driesvints.com" + "homepage": "http://driesvints.com", + "role": "Creator" + }, + { + "name": "Cristian Tabacitu", + "email": "hello@tabacitu.ro", + "homepage": "http://tabacitu.ro", + "role": "Maintainer" } ], "description": "Prologue Alerts is a package that handles global site messages.", @@ -2270,24 +3871,28 @@ "laravel", "messages" ], - "time": "2018-02-08T11:29:22+00:00" + "support": { + "issues": "https://github.com/prologuephp/alerts/issues", + "source": "https://github.com/prologuephp/alerts/tree/1.1.0" + }, + "time": "2023-02-01T06:54:14+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { @@ -2295,6 +3900,103 @@ "dev-master": "1.0.x-dev" } }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -2307,7 +4009,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -2319,7 +4021,168 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", @@ -2369,34 +4232,37 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", - "version": "1.1.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2406,7 +4272,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -2416,29 +4282,32 @@ "psr", "psr-3" ], - "time": "2018-11-20T15:27:04+00:00" + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" }, { "name": "psr/simple-cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -2453,7 +4322,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -2464,43 +4333,44 @@ "psr-16", "simple-cache" ], - "time": "2017-10-23T01:57:42+00:00" + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" }, { "name": "psy/psysh", - "version": "v0.9.9", + "version": "v0.11.12", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "9aaf29575bb8293206bb0420c1e1c87ff2ffa94e" + "reference": "52cb7c47d403c31c0adc9bf7710fc355f93c20f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/9aaf29575bb8293206bb0420c1e1c87ff2ffa94e", - "reference": "9aaf29575bb8293206bb0420c1e1c87ff2ffa94e", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/52cb7c47d403c31c0adc9bf7710fc355f93c20f7", + "reference": "52cb7c47d403c31c0adc9bf7710fc355f93c20f7", "shasum": "" }, "require": { - "dnoegel/php-xdg-base-dir": "0.1", "ext-json": "*", "ext-tokenizer": "*", - "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*", - "nikic/php-parser": "~1.3|~2.0|~3.0|~4.0", - "php": ">=5.4.0", - "symfony/console": "~2.3.10|^2.4.2|~3.0|~4.0", - "symfony/var-dumper": "~2.7|~3.0|~4.0" + "nikic/php-parser": "^4.0 || ^3.1", + "php": "^8.0 || ^7.0.8", + "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2", - "hoa/console": "~2.15|~3.16", - "phpunit/phpunit": "~4.8.35|~5.0|~6.0|~7.0" + "bamarni/composer-bin-plugin": "^1.2" }, "suggest": { "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", - "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." }, "bin": [ "bin/psysh" @@ -2508,7 +4378,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-develop": "0.9.x-dev" + "dev-main": "0.11.x-dev" } }, "autoload": { @@ -2538,60 +4408,107 @@ "interactive", "shell" ], - "time": "2018-10-13T15:16:03+00:00" + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.11.12" + }, + "time": "2023-01-29T21:24:40+00:00" }, { - "name": "ramsey/uuid", - "version": "3.8.0", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0|9.99.99", - "php": "^5.4 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "replace": { - "rhumsaa/uuid": "self.version" + "php": ">=5.6" }, "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0|^6.5", - "squizlabs/php_codesniffer": "^2.3" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, - "suggest": { - "ext-ctype": "Provides support for PHP Ctype functions", - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "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": { - "branch-alias": { - "dev-master": "3.x-dev" + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" } }, "autoload": { "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Ramsey\\Collection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2599,50 +4516,150 @@ "MIT" ], "authors": [ - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - }, { "name": "Ben Ramsey", "email": "ben@benramsey.com", "homepage": "https://benramsey.com" } ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", + "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.3", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "433b2014e3979047db08a17a205f410ba3869cf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2", + "reference": "433b2014e3979047db08a17a205f410ba3869cf2", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10", + "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" ], - "time": "2018-07-19T23:38:55+00:00" + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.3" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2023-01-12T18:13:24+00:00" }, { "name": "s1lentium/iptools", - "version": "v1.1.1", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/S1lentium/IPTools.git", - "reference": "f6f8ab6132ca7443bd7cced1681f5066d725fd5f" + "reference": "88be1aaaab3c50fc131ebe778e246215ff006d8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/S1lentium/IPTools/zipball/f6f8ab6132ca7443bd7cced1681f5066d725fd5f", - "reference": "f6f8ab6132ca7443bd7cced1681f5066d725fd5f", + "url": "https://api.github.com/repos/S1lentium/IPTools/zipball/88be1aaaab3c50fc131ebe778e246215ff006d8e", + "reference": "88be1aaaab3c50fc131ebe778e246215ff006d8e", "shasum": "" }, "require": { "ext-bcmath": "*", - "php": ">=5.4.0" + "php": "^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "~1.0" + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^9.0" }, "type": "library", "autoload": { @@ -2671,190 +4688,33 @@ "network", "subnet" ], - "time": "2018-09-19T06:15:53+00:00" - }, - { - "name": "sofa/eloquence-base", - "version": "v5.6.2", - "source": { - "type": "git", - "url": "https://github.com/jarektkaczyk/eloquence-base.git", - "reference": "e941b7ff79ca9c77ef540b5f14a7f82a3acea5ba" + "support": { + "issues": "https://github.com/S1lentium/IPTools/issues", + "source": "https://github.com/S1lentium/IPTools/tree/v1.2.0" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jarektkaczyk/eloquence-base/zipball/e941b7ff79ca9c77ef540b5f14a7f82a3acea5ba", - "reference": "e941b7ff79ca9c77ef540b5f14a7f82a3acea5ba", - "shasum": "" - }, - "require": { - "illuminate/database": "^5.5", - "php": ">=7.0.0", - "sofa/hookable": "^5.5" - }, - "require-dev": { - "mockery/mockery": "0.9.4", - "phpunit/phpunit": "4.5.0", - "squizlabs/php_codesniffer": "2.3.3" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Sofa\\Eloquence\\BaseServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Sofa\\Eloquence\\": "src" - }, - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jarek Tkaczyk", - "role": "Developer", - "email": "jarek@softonsofa.com", - "homepage": "https://softonsofa.com/" - } - ], - "description": "Flexible Searchable, Mappable, Metable, Validation and more extensions for Laravel Eloquent ORM.", - "keywords": [ - "eloquent", - "laravel", - "mappable", - "metable", - "mutable", - "searchable" - ], - "time": "2018-03-10T12:37:43+00:00" - }, - { - "name": "sofa/eloquence-validable", - "version": "5.6", - "source": { - "type": "git", - "url": "https://github.com/jarektkaczyk/eloquence-validable.git", - "reference": "9d9ef65bf4a4952efb54b06ac0b04fc8893d5f95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jarektkaczyk/eloquence-validable/zipball/9d9ef65bf4a4952efb54b06ac0b04fc8893d5f95", - "reference": "9d9ef65bf4a4952efb54b06ac0b04fc8893d5f95", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "sofa/eloquence-base": "^5.5" - }, - "require-dev": { - "mockery/mockery": "0.9.4", - "phpunit/phpunit": "4.5.0", - "squizlabs/php_codesniffer": "2.3.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Sofa\\Eloquence\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jarek Tkaczyk", - "email": "jarek@softonsofa.com", - "homepage": "https://softonsofa.com/", - "role": "Developer" - } - ], - "description": "Flexible Searchable, Mappable, Metable, Validation and more extensions for Laravel Eloquent ORM.", - "keywords": [ - "eloquent", - "laravel", - "mappable", - "metable", - "mutable", - "searchable" - ], - "time": "2018-03-03T03:09:46+00:00" - }, - { - "name": "sofa/hookable", - "version": "5.6", - "source": { - "type": "git", - "url": "https://github.com/jarektkaczyk/hookable.git", - "reference": "c6f03e5e742d539755f8c7993ee96e907593a668" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jarektkaczyk/hookable/zipball/c6f03e5e742d539755f8c7993ee96e907593a668", - "reference": "c6f03e5e742d539755f8c7993ee96e907593a668", - "shasum": "" - }, - "require": { - "illuminate/database": "^5.3", - "php": ">=5.6.4" - }, - "require-dev": { - "crysalead/kahlan": "~1.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Sofa\\Hookable\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jarek Tkaczyk", - "role": "Developer", - "email": "jarek@softonsofa.com", - "homepage": "http://softonsofa.com/" - } - ], - "description": "Laravel Eloquent hooks system.", - "keywords": [ - "eloquent", - "laravel" - ], - "time": "2018-03-03T02:55:49+00:00" + "time": "2022-08-17T14:28:59+00:00" }, { "name": "spatie/fractalistic", - "version": "2.7.2", + "version": "2.9.5", "source": { "type": "git", "url": "https://github.com/spatie/fractalistic.git", - "reference": "5b5710b748beb2c1d5c272f4d3598d44b5b59fc9" + "reference": "6f12686a03d035f4558d166989c62aa93bde2151" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/fractalistic/zipball/5b5710b748beb2c1d5c272f4d3598d44b5b59fc9", - "reference": "5b5710b748beb2c1d5c272f4d3598d44b5b59fc9", + "url": "https://api.github.com/repos/spatie/fractalistic/zipball/6f12686a03d035f4558d166989c62aa93bde2151", + "reference": "6f12686a03d035f4558d166989c62aa93bde2151", "shasum": "" }, "require": { - "league/fractal": "^0.17.0", - "php": "^7.0" + "league/fractal": "^0.20.1", + "php": "^7.4|^8.0" }, "require-dev": { "illuminate/pagination": "~5.3.0|~5.4.0", - "phpunit/phpunit": "^5.7.21" + "phpunit/phpunit": "^9.0" }, "type": "library", "autoload": { @@ -2883,31 +4743,45 @@ "spatie", "transform" ], - "time": "2018-10-08T09:18:33+00:00" + "support": { + "issues": "https://github.com/spatie/fractalistic/issues", + "source": "https://github.com/spatie/fractalistic/tree/2.9.5" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-04-21T12:26:22+00:00" }, { "name": "spatie/laravel-fractal", - "version": "5.4.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-fractal.git", - "reference": "2931881cac3155ceb798f2fd1e55bd152576682b" + "reference": "28ea9803d2cba7a7144336a2ba62275316c122c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/2931881cac3155ceb798f2fd1e55bd152576682b", - "reference": "2931881cac3155ceb798f2fd1e55bd152576682b", + "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/28ea9803d2cba7a7144336a2ba62275316c122c8", + "reference": "28ea9803d2cba7a7144336a2ba62275316c122c8", "shasum": "" }, "require": { - "illuminate/contracts": "~5.5.0|~5.6.0|~5.7.0", - "illuminate/support": "~5.5.0|~5.6.0|~5.7.0", - "php": "^7.0", - "spatie/fractalistic": "^2.5" + "illuminate/contracts": "^8.0|^9.0|^10.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "league/fractal": "^0.20.1|^0.20", + "nesbot/carbon": "^2.63", + "php": "^8.0", + "spatie/fractalistic": "^2.9.5|^2.9", + "spatie/laravel-package-tools": "^1.11" }, "require-dev": { - "orchestra/testbench": "~3.5.0|~3.6.0|~3.7.0", - "phpunit/phpunit": "^6.2|^7.0" + "ext-json": "*", + "orchestra/testbench": "^7.0|^8.0", + "pestphp/pest": "^1.22" }, "type": "library", "extra": { @@ -2916,17 +4790,17 @@ "Spatie\\Fractal\\FractalServiceProvider" ], "aliases": { - "Fractal": "Spatie\\Fractal\\FractalFacade" + "Fractal": "Spatie\\Fractal\\Facades\\Fractal" } } }, "autoload": { - "psr-4": { - "Spatie\\Fractal\\": "src" - }, "files": [ "src/helpers.php" - ] + ], + "psr-4": { + "Spatie\\Fractal\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2951,31 +4825,169 @@ "spatie", "transform" ], - "time": "2018-09-28T16:17:34+00:00" + "support": { + "source": "https://github.com/spatie/laravel-fractal/tree/6.0.3" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2023-01-30T22:11:18+00:00" }, { - "name": "staudenmeir/belongs-to-through", - "version": "v2.3.2", + "name": "spatie/laravel-package-tools", + "version": "1.14.1", "source": { "type": "git", - "url": "https://github.com/staudenmeir/belongs-to-through.git", - "reference": "2ba1ff76353058d2b4d395e725617d97fd103ab0" + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "b477dd2f89d0751f0e51ffb3a70181f0bc7ab8df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/belongs-to-through/zipball/2ba1ff76353058d2b4d395e725617d97fd103ab0", - "reference": "2ba1ff76353058d2b4d395e725617d97fd103ab0", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/b477dd2f89d0751f0e51ffb3a70181f0bc7ab8df", + "reference": "b477dd2f89d0751f0e51ffb3a70181f0bc7ab8df", "shasum": "" }, "require": { - "illuminate/database": "~5.0" + "illuminate/contracts": "^9.28|^10.0", + "php": "^8.0" }, "require-dev": { - "fabpot/php-cs-fixer": "^1.11", - "orchestra/testbench": "~3.0", - "phpunit/php-code-coverage": "^3.3", - "phpunit/phpunit": "~5.0", - "satooshi/php-coveralls": "^1.0" + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0", + "pestphp/pest": "^1.22", + "phpunit/phpunit": "^9.5.24", + "spatie/pest-plugin-test-time": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.14.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-01-27T15:33:45+00:00" + }, + { + "name": "spatie/laravel-query-builder", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-query-builder.git", + "reference": "5908f58fdff70fb600982b58c187e84855e2b5bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/5908f58fdff70fb600982b58c187e84855e2b5bc", + "reference": "5908f58fdff70fb600982b58c187e84855e2b5bc", + "shasum": "" + }, + "require": { + "illuminate/database": "^9.0|^10.0", + "illuminate/http": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.11" + }, + "require-dev": { + "ext-json": "*", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^7.0|^8.0", + "pestphp/pest": "^1.20", + "spatie/laravel-ray": "^1.28" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\QueryBuilder\\QueryBuilderServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\QueryBuilder\\": "src", + "Spatie\\QueryBuilder\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily build Eloquent queries from API requests", + "homepage": "https://github.com/spatie/laravel-query-builder", + "keywords": [ + "laravel-query-builder", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-query-builder/issues", + "source": "https://github.com/spatie/laravel-query-builder" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2023-01-24T23:33:10+00:00" + }, + { + "name": "staudenmeir/belongs-to-through", + "version": "v2.13", + "source": { + "type": "git", + "url": "https://github.com/staudenmeir/belongs-to-through.git", + "reference": "e777027d648971c9686f9d7c284f324db7cce3c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staudenmeir/belongs-to-through/zipball/e777027d648971c9686f9d7c284f324db7cce3c0", + "reference": "e777027d648971c9686f9d7c284f324db7cce3c0", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0", + "php": "^8.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.27" }, "type": "library", "autoload": { @@ -2991,121 +5003,72 @@ { "name": "Rahul Kadyan", "email": "hi@znck.me" - } - ], - "description": "Adds belongsToThrough relation to laravel models", - "homepage": "https://github.com/staudenmeir/belongs-to-through", - "keywords": [ - "belongsToThrough", - "eloquent", - "laravel", - "model", - "models", - "znck" - ], - "time": "2019-02-01T14:33:18+00:00" - }, - { - "name": "swiftmailer/swiftmailer", - "version": "v6.1.3", - "source": { - "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "8ddcb66ac10c392d3beb54829eef8ac1438595f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8ddcb66ac10c392d3beb54829eef8ac1438595f4", - "reference": "8ddcb66ac10c392d3beb54829eef8ac1438595f4", - "shasum": "" - }, - "require": { - "egulias/email-validator": "~2.0", - "php": ">=7.0.0" - }, - "require-dev": { - "mockery/mockery": "~0.9.1", - "symfony/phpunit-bridge": "~3.3@dev" - }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses", - "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-dev" - } - }, - "autoload": { - "files": [ - "lib/swift_required.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Corbyn" }, { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Jonas Staudenmeir", + "email": "mail@jonas-staudenmeir.de" } ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" + "description": "Laravel Eloquent BelongsToThrough relationships", + "support": { + "issues": "https://github.com/staudenmeir/belongs-to-through/issues", + "source": "https://github.com/staudenmeir/belongs-to-through/tree/v2.13" + }, + "funding": [ + { + "url": "https://paypal.me/JonasStaudenmeir", + "type": "custom" + } ], - "time": "2018-09-11T07:12:52+00:00" + "time": "2023-01-18T12:40:35+00:00" }, { "name": "symfony/console", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "432122af37d8cd52fba1b294b11976e0d20df595" + "reference": "3e294254f2191762c1d137aed4b94e966965e985" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/432122af37d8cd52fba1b294b11976e0d20df595", - "reference": "432122af37d8cd52fba1b294b11976e0d20df595", + "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985", + "reference": "3e294254f2191762c1d137aed4b94e966965e985", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" }, "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" }, "suggest": { - "psr/log-implementation": "For using the console logger", + "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" @@ -3128,33 +5091,51 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", - "time": "2018-10-31T09:30:44+00:00" + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "d67de79a70a27d93c92c47f37ece958bf8de4d8a" + "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/d67de79a70a27d93c92c47f37ece958bf8de4d8a", - "reference": "d67de79a70a27d93c92c47f37ece958bf8de4d8a", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", + "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" @@ -3168,56 +5149,138 @@ "MIT" ], "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony CssSelector Component", + "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", - "time": "2018-10-02T16:36:10+00:00" + "support": { + "source": "https://github.com/symfony/css-selector/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/debug", - "version": "v4.1.7", + "name": "symfony/deprecation-contracts", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "19090917b848a799cbae4800abf740fe4eb71c1d" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/19090917b848a799cbae4800abf740fe4eb71c1d", - "reference": "19090917b848a799cbae4800abf740fe4eb71c1d", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { - "php": "^7.1.3", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": "<3.4" - }, - "require-dev": { - "symfony/http-kernel": "~3.4|~4.0" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "0092696af0be8e6124b042fbe2890ca1788d7b28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/0092696af0be8e6124b042fbe2890ca1788d7b28", + "reference": "0092696af0be8e6124b042fbe2890ca1788d7b28", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Debug\\": "" + "Symfony\\Component\\ErrorHandler\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3237,47 +5300,67 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Debug Component", + "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", - "time": "2018-10-31T09:09:42+00:00" + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "552541dad078c85d9414b09c041ede488b456cd5" + "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/552541dad078c85d9414b09c041ede488b456cd5", - "reference": "552541dad078c85d9414b09c041ede488b456cd5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", + "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2|^3" }, "conflict": { - "symfony/dependency-injection": "<3.4" + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" }, "suggest": { "symfony/dependency-injection": "", "symfony/http-kernel": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" @@ -3300,33 +5383,127 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", - "time": "2018-10-10T13:52:42+00:00" + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/finder", - "version": "v4.1.7", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "1f17195b44543017a9c9b2d437c670627e96ad06" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06", - "reference": "1f17195b44543017a9c9b2d437c670627e96ad06", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0782b0b52a737a05b4383d0df35a474303cabdae", + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" @@ -3349,38 +5526,228 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "time": "2018-10-03T08:47:56+00:00" + "support": { + "source": "https://github.com/symfony/finder/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-20T17:45:48+00:00" }, { - "name": "symfony/http-foundation", - "version": "v4.1.7", + "name": "symfony/http-client", + "version": "v6.2.6", "source": { "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "82d494c1492b0dd24bbc5c2d963fb02eb44491af" + "url": "https://github.com/symfony/http-client.git", + "reference": "6efa9a7521ab7d031a82cf0a759484d1b02a6ad9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/82d494c1492b0dd24bbc5c2d963fb02eb44491af", - "reference": "82d494c1492b0dd24bbc5c2d963fb02eb44491af", + "url": "https://api.github.com/repos/symfony/http-client/zipball/6efa9a7521ab7d031a82cf0a759484d1b02a6ad9", + "reference": "6efa9a7521ab7d031a82cf0a759484d1b02a6ad9", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.1" + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-client-contracts": "^3", + "symfony/service-contracts": "^1.0|^2|^3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" }, "require-dev": { - "predis/predis": "~1.0", - "symfony/expression-language": "~3.4|~4.0" + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.2.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-30T15:46:28+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "c5f587eb445224ddfeb05b5ee703476742d730bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/c5f587eb445224ddfeb05b5ee703476742d730bf", + "reference": "c5f587eb445224ddfeb05b5ee703476742d730bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "suggest": { + "symfony/http-client-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "e8dd1f502bc2b3371d05092aa233b064b03ce7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e8dd1f502bc2b3371d05092aa233b064b03ce7ed", + "reference": "e8dd1f502bc2b3371d05092aa233b064b03ce7ed", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.1" + }, + "conflict": { + "symfony/cache": "<6.2" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/cache": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", + "symfony/mime": "^5.4|^6.0", + "symfony/rate-limiter": "^5.2|^6.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" + }, + "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" @@ -3403,71 +5770,95 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony HttpFoundation Component", + "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", - "time": "2018-10-31T09:09:42+00:00" + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.2.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-30T15:46:28+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.1.7", + "version": "v6.2.6", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "958be64ab13b65172ad646ef5ae20364c2305fae" + "reference": "7122db07b0d8dbf0de682267c84217573aee3ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/958be64ab13b65172ad646ef5ae20364c2305fae", - "reference": "958be64ab13b65172ad646ef5ae20364c2305fae", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7122db07b0d8dbf0de682267c84217573aee3ea7", + "reference": "7122db07b0d8dbf0de682267c84217573aee3ea7", "shasum": "" }, "require": { - "php": "^7.1.3", - "psr/log": "~1.0", - "symfony/debug": "~3.4|~4.0", - "symfony/event-dispatcher": "~4.1", - "symfony/http-foundation": "^4.1.1", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/error-handler": "^6.1", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<4.1", - "symfony/var-dumper": "<4.1.1", - "twig/twig": "<1.34|<2.4,>=2" + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.2", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<5.4", + "twig/twig": "<2.13" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/cache": "~1.0", - "symfony/browser-kit": "~3.4|~4.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dependency-injection": "^4.1", - "symfony/dom-crawler": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "~3.4|~4.0", - "symfony/var-dumper": "^4.1.1" + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/config": "^6.1", + "symfony/console": "^5.4|^6.0", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dependency-injection": "^6.2", + "symfony/dom-crawler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/process": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" }, "suggest": { "symfony/browser-kit": "", "symfony/config": "", "symfony/console": "", - "symfony/dependency-injection": "", - "symfony/var-dumper": "" + "symfony/dependency-injection": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\HttpKernel\\": "" @@ -3490,42 +5881,69 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony HttpKernel Component", + "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", - "time": "2018-11-03T11:11:23+00:00" + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.2.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-01T08:32:25+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.10.0", + "name": "symfony/mailer", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "url": "https://github.com/symfony/mailer.git", + "reference": "29729ac0b4e5113f24c39c46746bd6afb79e0aaa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/mailer/zipball/29729ac0b4e5113f24c39c46746bd6afb79e0aaa", + "reference": "29729ac0b4e5113f24c39c46746bd6afb79e0aaa", "shasum": "" }, "require": { - "php": ">=5.3.3" + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/mime": "^6.2", + "symfony/service-contracts": "^1.1|^2|^3" }, - "suggest": { - "ext-ctype": "For best performance" + "conflict": { + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/messenger": "^6.2", + "symfony/twig-bridge": "^6.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Component\\Mailer\\": "" }, - "files": [ - "bootstrap.php" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3533,13 +5951,237 @@ "MIT" ], "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-10T18:53:53+00:00" + }, + { + "name": "symfony/mailgun-mailer", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailgun-mailer.git", + "reference": "d1eb7283e30752f2802ced37bffdee2c67cad42a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/d1eb7283e30752f2802ced37bffdee2c67cad42a", + "reference": "d1eb7283e30752f2802ced37bffdee2c67cad42a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/mailer": "^5.4|^6.0" + }, + "require-dev": { + "symfony/http-client": "^5.4|^6.0" + }, + "type": "symfony-mailer-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Mailgun Mailer Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailgun-mailer/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "4b7b349f67d15cd0639955c8179a76c89f6fd610" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/4b7b349f67d15cd0639955c8179a76c89f6fd610", + "reference": "4b7b349f67d15cd0639955c8179a76c89f6fd610", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.2" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/serializer": "^6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-10T18:53:53+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -3550,24 +6192,296 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", + "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" }, "suggest": { "ext-mbstring": "For best performance" @@ -3575,16 +6489,20 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3609,94 +6527,59 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" - }, - { - "name": "symfony/polyfill-php56", - "version": "v1.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php56.git", - "reference": "ff208829fe1aa48ab9af356992bb7199fed551af" + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/ff208829fe1aa48ab9af356992bb7199fed551af", - "reference": "ff208829fe1aa48ab9af356992bb7199fed551af", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/polyfill-util": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php56\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2018-09-21T06:26:08+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.10.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631" + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", - "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3720,41 +6603,72 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-util", - "version": "v1.10.0", + "name": "symfony/polyfill-php80", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-util.git", - "reference": "3b58903eae668d348a7126f999b0da0f2f93611c" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/3b58903eae668d348a7126f999b0da0f2f93611c", - "reference": "3b58903eae668d348a7126f999b0da0f2f93611c", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Polyfill\\Util\\": "" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -3764,39 +6678,198 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony utilities for portability of PHP codes", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "compat", "compatibility", "polyfill", + "portable", "shim" ], - "time": "2018-09-30T16:36:12+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/process", - "version": "v4.1.7", + "name": "symfony/polyfill-uuid", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "3e83acef94d979b1de946599ef86b3a352abcdc9" + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3e83acef94d979b1de946599ef86b3a352abcdc9", - "reference": "3e83acef94d979b1de946599ef86b3a352abcdc9", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166", + "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/postmark-mailer", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/postmark-mailer.git", + "reference": "b6630e287f94fbc1f0f0a20a3a147a69ac8f862b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/postmark-mailer/zipball/b6630e287f94fbc1f0f0a20a3a147a69ac8f862b", + "reference": "b6630e287f94fbc1f0f0a20a3a147a69ac8f862b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/mailer": "^5.4|^6.0" + }, + "require-dev": { + "symfony/http-client": "^5.4|^6.0" + }, + "type": "symfony-mailer-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Postmark\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Postmark Mailer Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/postmark-mailer/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:55+00:00" + }, + { + "name": "symfony/process", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", + "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" @@ -3819,55 +6892,66 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "time": "2018-10-14T20:48:13+00:00" + "support": { + "source": "https://github.com/symfony/process/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/routing", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "d4a3c14cfbe6b9c05a1d6e948654022d4d1ad3fd" + "reference": "589bd742d5d03c192c8521911680fe88f61712fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/d4a3c14cfbe6b9c05a1d6e948654022d4d1ad3fd", - "reference": "d4a3c14cfbe6b9c05a1d6e948654022d4d1ad3fd", + "url": "https://api.github.com/repos/symfony/routing/zipball/589bd742d5d03c192c8521911680fe88f61712fe", + "reference": "589bd742d5d03c192c8521911680fe88f61712fe", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1" }, "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" + "doctrine/annotations": "<1.12", + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" }, "require-dev": { - "doctrine/annotations": "~1.0", - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "doctrine/annotations": "^1.12|^2", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.2", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" }, "suggest": { - "doctrine/annotations": "For using the annotation loader", "symfony/config": "For using the all-in-one router or any loader", - "symfony/dependency-injection": "For loading routes from a service", "symfony/expression-language": "For using expression matching", "symfony/http-foundation": "For using a Symfony Request object", "symfony/yaml": "For using the YAML loader" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Routing\\": "" @@ -3890,7 +6974,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Routing Component", + "description": "Maps an HTTP request to a set of configuration variables", "homepage": "https://symfony.com", "keywords": [ "router", @@ -3898,52 +6982,252 @@ "uri", "url" ], - "time": "2018-10-28T18:38:52+00:00" + "support": { + "source": "https://github.com/symfony/routing/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/translation", - "version": "v4.1.7", + "name": "symfony/service-contracts", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", - "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/string", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" + "symfony/translation-contracts": "<2.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/intl": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/60556925a703cfbc1581cde3b3f35b0bb0ea904c", + "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" }, "suggest": { + "nikic/php-parser": "To use PhpAstExtractor", "psr/log-implementation": "To use logging capability in translator", "symfony/config": "", "symfony/yaml": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Symfony\\Component\\Translation\\": "" }, @@ -3965,37 +7249,210 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Translation Component", + "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", - "time": "2018-10-28T18:38:52+00:00" + "support": { + "source": "https://github.com/symfony/translation/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-05T07:00:27+00:00" }, { - "name": "symfony/var-dumper", - "version": "v4.1.7", + "name": "symfony/translation-contracts", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "60319b45653580b0cdacca499344577d87732f16" + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "68cce71402305a015f8c1589bfada1280dc64fe7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/60319b45653580b0cdacca499344577d87732f16", - "reference": "60319b45653580b0cdacca499344577d87732f16", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/68cce71402305a015f8c1589bfada1280dc64fe7", + "reference": "68cce71402305a015f8c1589bfada1280dc64fe7", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php72": "~1.5" + "php": ">=8.1" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/uid", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "8ace895bded57d6496638c9b2d3b788e05b7395b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/8ace895bded57d6496638c9b2d3b788e05b7395b", + "reference": "8ace895bded57d6496638c9b2d3b788e05b7395b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "44b7b81749fd20c1bdf4946c041050e22bc8da27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/44b7b81749fd20c1bdf4946c041050e22bc8da27", + "reference": "44b7b81749fd20c1bdf4946c041050e22bc8da27", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/console": "<3.4" + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<5.4" }, "require-dev": { "ext-iconv": "*", - "symfony/process": "~3.4|~4.0", - "twig/twig": "~1.34|~2.4" + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", @@ -4006,11 +7463,6 @@ "Resources/bin/var-dump-server" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "files": [ "Resources/functions/dump.php" @@ -4036,34 +7488,127 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony mechanism for exploring and dumping PHP variables", + "description": "Provides mechanisms for walking through any arbitrary PHP variable", "homepage": "https://symfony.com", "keywords": [ "debug", "dump" ], - "time": "2018-10-02T16:36:10+00:00" + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-20T17:45:48+00:00" }, { - "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.1", + "name": "symfony/yaml", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757" + "url": "https://github.com/symfony/yaml.git", + "reference": "2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757", - "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19", + "reference": "2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0" + "php": ">=8.1", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "symfony/console": "^5.4|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-10T18:53:53+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "2.2.6", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5 || ^7.0 || ^8.0", + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" }, "type": "library", "extra": { @@ -4089,32 +7634,51 @@ ], "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", - "time": "2017-11-27T11:13:29+00:00" + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" + }, + "time": "2023-01-03T09:29:04+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v2.5.1", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e" + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", - "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", "shasum": "" }, "require": { - "php": ">=5.3.9" + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.0" + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "5.5-dev" } }, "autoload": { @@ -4127,10 +7691,15 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Vance Lucas", "email": "vance@vancelucas.com", - "homepage": "http://www.vancelucas.com" + "homepage": "https://github.com/vlucas" } ], "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", @@ -4139,33 +7708,125 @@ "env", "environment" ], - "time": "2018-07-29T20:33:41+00:00" + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2022-10-16T01:01:54+00:00" }, { - "name": "webmozart/assert", - "version": "1.3.0", + "name": "voku/portable-ascii", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.0.0" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-03-08T17:03:00+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -4189,116 +7850,58 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" } ], "packages-dev": [ - { - "name": "barryvdh/laravel-debugbar", - "version": "v3.2.1", - "source": { - "type": "git", - "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "9d5caf43c5f3a3aea2178942f281054805872e7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/9d5caf43c5f3a3aea2178942f281054805872e7c", - "reference": "9d5caf43c5f3a3aea2178942f281054805872e7c", - "shasum": "" - }, - "require": { - "illuminate/routing": "5.5.x|5.6.x|5.7.x", - "illuminate/session": "5.5.x|5.6.x|5.7.x", - "illuminate/support": "5.5.x|5.6.x|5.7.x", - "maximebf/debugbar": "~1.15.0", - "php": ">=7.0", - "symfony/debug": "^3|^4", - "symfony/finder": "^3|^4" - }, - "require-dev": { - "laravel/framework": "5.5.x" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - }, - "laravel": { - "providers": [ - "Barryvdh\\Debugbar\\ServiceProvider" - ], - "aliases": { - "Debugbar": "Barryvdh\\Debugbar\\Facade" - } - } - }, - "autoload": { - "psr-4": { - "Barryvdh\\Debugbar\\": "src/" - }, - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "PHP Debugbar integration for Laravel", - "keywords": [ - "debug", - "debugbar", - "laravel", - "profiler", - "webprofiler" - ], - "time": "2018-11-09T08:37:55+00:00" - }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.5.2", + "version": "v2.13.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "981ff45b43e0cf808af0a5a5f40f6369e0e29499" + "reference": "81d5b223ff067a1f38e14c100997e153b837fe4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/981ff45b43e0cf808af0a5a5f40f6369e0e29499", - "reference": "981ff45b43e0cf808af0a5a5f40f6369e0e29499", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/81d5b223ff067a1f38e14c100997e153b837fe4a", + "reference": "81d5b223ff067a1f38e14c100997e153b837fe4a", "shasum": "" }, "require": { - "barryvdh/reflection-docblock": "^2.0.4", - "composer/composer": "^1.6", - "illuminate/console": "^5.5,<5.8", - "illuminate/filesystem": "^5.5,<5.8", - "illuminate/support": "^5.5,<5.8", - "php": ">=7" + "barryvdh/reflection-docblock": "^2.0.6", + "composer/class-map-generator": "^1.0", + "doctrine/dbal": "^2.6 || ^3", + "ext-json": "*", + "illuminate/console": "^8 || ^9 || ^10", + "illuminate/filesystem": "^8 || ^9 || ^10", + "illuminate/support": "^8 || ^9 || ^10", + "nikic/php-parser": "^4.7", + "php": "^7.3 || ^8.0", + "phpdocumentor/type-resolver": "^1.1.0" }, "require-dev": { - "doctrine/dbal": "~2.3", - "illuminate/config": "^5.1,<5.8", - "illuminate/view": "^5.1,<5.8", - "phpro/grumphp": "^0.14", - "phpunit/phpunit": "4.*", - "scrutinizer/ocular": "~1.1", - "squizlabs/php_codesniffer": "^3" + "ext-pdo_sqlite": "*", + "friendsofphp/php-cs-fixer": "^2", + "illuminate/config": "^8 || ^9 || ^10", + "illuminate/view": "^8 || ^9 || ^10", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^6 || ^7 || ^8", + "phpunit/phpunit": "^8.5 || ^9", + "spatie/phpunit-snapshot-assertions": "^3 || ^4", + "vimeo/psalm": "^3.12" }, "suggest": { - "doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)" + "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.12-dev" }, "laravel": { "providers": [ @@ -4333,27 +7936,41 @@ "phpstorm", "sublime" ], - "time": "2018-10-06T09:35:51+00:00" + "support": { + "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.13.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-02-04T13:56:40+00:00" }, { "name": "barryvdh/reflection-docblock", - "version": "v2.0.5", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "reference": "64165bd4ba9a550d11ea57569463b7c722dc6b0a" + "reference": "bf44b757feb8ba1734659029357646466ded673e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/64165bd4ba9a550d11ea57569463b7c722dc6b0a", - "reference": "64165bd4ba9a550d11ea57569463b7c722dc6b0a", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/bf44b757feb8ba1734659029357646466ded673e", + "reference": "bf44b757feb8ba1734659029357646466ded673e", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~4.0,<4.5" + "phpunit/phpunit": "^8.5.14|^9" }, "suggest": { "dflydev/markdown": "~1.0", @@ -4382,137 +7999,47 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2018-10-25T19:09:52+00:00" + "support": { + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.1.0" + }, + "time": "2022-10-31T15:35:43+00:00" }, { - "name": "codedungeon/php-cli-colors", - "version": "1.10.7", + "name": "composer/class-map-generator", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/mikeerickson/php-cli-colors.git", - "reference": "5649ef76ec0c9ed626e95bf40fdfaf4b8efcf79b" + "url": "https://github.com/composer/class-map-generator.git", + "reference": "1e1cb2b791facb2dfe32932a7718cf2571187513" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mikeerickson/php-cli-colors/zipball/5649ef76ec0c9ed626e95bf40fdfaf4b8efcf79b", - "reference": "5649ef76ec0c9ed626e95bf40fdfaf4b8efcf79b", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": ">=5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codedungeon\\PHPCliColors\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike Erickson", - "email": "codedungeon@gmail.com" - } - ], - "description": "PHP Package for using color output in CLI commands", - "homepage": "https://github.com/mikeerickson/php-cli-colors", - "keywords": [ - "color", - "colors", - "composer", - "package", - "php" - ], - "time": "2018-05-17T01:34:14+00:00" - }, - { - "name": "codedungeon/phpunit-result-printer", - "version": "0.17.1", - "source": { - "type": "git", - "url": "https://github.com/mikeerickson/phpunit-pretty-result-printer.git", - "reference": "aac73dbc502e70d42059d74a5aced6911982797b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mikeerickson/phpunit-pretty-result-printer/zipball/aac73dbc502e70d42059d74a5aced6911982797b", - "reference": "aac73dbc502e70d42059d74a5aced6911982797b", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/1e1cb2b791facb2dfe32932a7718cf2571187513", + "reference": "1e1cb2b791facb2dfe32932a7718cf2571187513", "shasum": "" }, "require": { - "codedungeon/php-cli-colors": "^1.10", - "hassankhan/config": "^0.10.0", - "php": "^7.1", - "symfony/yaml": "^2.7|^3.0|^4.0" + "composer/pcre": "^2 || ^3", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6" }, "require-dev": { - "phpunit/phpunit": "7.1.1", - "spatie/phpunit-watcher": "^1.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codedungeon\\PHPUnitPrettyResultPrinter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike Erickson", - "email": "codedungeon@gmail.com" - } - ], - "description": "PHPUnit Pretty Result Printer", - "keywords": [ - "TDD", - "composer", - "package", - "phpunit", - "printer", - "result-printer", - "testing" - ], - "time": "2018-05-09T02:10:52+00:00" - }, - { - "name": "composer/ca-bundle", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "8afa52cd417f4ec417b4bfe86b68106538a87660" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8afa52cd417f4ec417b4bfe86b68106538a87660", - "reference": "8afa52cd417f4ec417b4bfe86b68106538a87660", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5", - "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0" + "phpstan/phpstan": "^1.6", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/filesystem": "^5.4 || ^6", + "symfony/phpunit-bridge": "^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "Composer\\CaBundle\\": "src" + "Composer\\ClassMapGenerator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4523,124 +8050,129 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "description": "Utilities to scan PHP code and generate class maps.", "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" + "classmap" ], - "time": "2018-10-18T06:09:13+00:00" - }, - { - "name": "composer/composer", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/composer/composer.git", - "reference": "e965b9aaa8854c3067f1ed2ae45f436572d73eb7" + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.0.0" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/e965b9aaa8854c3067f1ed2ae45f436572d73eb7", - "reference": "e965b9aaa8854c3067f1ed2ae45f436572d73eb7", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", - "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0", - "seld/jsonlint": "^1.4", - "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0" - }, - "conflict": { - "symfony/console": "2.8.38" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7", - "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" - }, - "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" - }, - "bin": [ - "bin/composer" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\": "src/Composer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "url": "https://packagist.com", + "type": "custom" }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-06-19T11:31:27+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http://seld.be" } ], - "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", - "homepage": "https://getcomposer.org/", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "autoload", - "dependency", - "package" + "PCRE", + "preg", + "regex", + "regular expression" ], - "time": "2018-11-01T09:05:06+00:00" + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" }, { "name": "composer/semver", - "version": "1.4.2", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -4676,89 +8208,50 @@ "validation", "versioning" ], - "time": "2016-08-30T16:08:34+00:00" - }, - { - "name": "composer/spdx-licenses", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/composer/spdx-licenses.git", - "reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2" + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7a9556b22bd9d4df7cad89876b00af58ef20d3a2", - "reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Spdx\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "url": "https://packagist.com", + "type": "custom" }, { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "url": "https://github.com/composer", + "type": "github" }, { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "description": "SPDX licenses list and validation library.", - "keywords": [ - "license", - "spdx", - "validator" - ], - "time": "2018-11-01T09:45:54+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.3.0", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c" + "reference": "ced299686f41dce890debac69273b47ffe98a40c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" }, "type": "library", "autoload": { @@ -4776,41 +8269,64 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2018-08-31T19:07:57+00:00" + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" }, { "name": "doctrine/annotations", - "version": "v1.6.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" @@ -4821,6 +8337,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -4829,10 +8349,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -4843,47 +8359,63 @@ } ], "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], - "time": "2017-12-06T07:11:42+00:00" + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" + }, + "time": "2023-02-02T22:02:53+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.1.0", + "name": "fakerphp/faker", + "version": "v1.21.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", + "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" }, "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-main": "v1.21-dev" } }, "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "Faker\\": "src/Faker/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4892,41 +8424,43 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "name": "François Zaninotto" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "description": "Faker is a PHP library that generates fake data for you.", "keywords": [ - "constructor", - "instantiate" + "data", + "faker", + "fixtures" ], - "time": "2017-07-22T11:58:36+00:00" + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + }, + "time": "2022-12-13T13:54:32+00:00" }, { "name": "filp/whoops", - "version": "2.3.1", + "version": "2.14.6", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7" + "reference": "f7948baaa0330277c729714910336383286305da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/bc0fd11bc455cc20ee4b5edabc63ebbf859324c7", - "reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7", + "url": "https://api.github.com/repos/filp/whoops/zipball/f7948baaa0330277c729714910336383286305da", + "reference": "f7948baaa0330277c729714910336383286305da", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0", - "psr/log": "^1.0.1" + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0" + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -4935,7 +8469,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.7-dev" } }, "autoload": { @@ -4964,58 +8498,71 @@ "throwable", "whoops" ], - "time": "2018-10-23T09:00:00+00:00" + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.14.6" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2022-11-02T16:23:29+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.15.1", + "version": "v3.14.4", "source": { "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "20064511ab796593a3990669eff5f5b535001f7c" + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "1b3d9dba63d93b8a202c31e824748218781eae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/20064511ab796593a3990669eff5f5b535001f7c", - "reference": "20064511ab796593a3990669eff5f5b535001f7c", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1b3d9dba63d93b8a202c31e824748218781eae6b", + "reference": "1b3d9dba63d93b8a202c31e824748218781eae6b", "shasum": "" }, "require": { - "composer/semver": "^1.4", - "composer/xdebug-handler": "^1.2", - "doctrine/annotations": "^1.2", + "composer/semver": "^3.3", + "composer/xdebug-handler": "^3.0.3", + "doctrine/annotations": "^2", + "doctrine/lexer": "^2 || ^3", "ext-json": "*", "ext-tokenizer": "*", - "php": "^5.6 || ^7.0", - "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6", - "symfony/event-dispatcher": "^3.0 || ^4.0", - "symfony/filesystem": "^3.0 || ^4.0", - "symfony/finder": "^3.0 || ^4.0", - "symfony/options-resolver": "^3.0 || ^4.0", - "symfony/polyfill-php70": "^1.0", - "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0", - "symfony/stopwatch": "^3.0 || ^4.0" + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.27", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" }, "require-dev": { - "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", - "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.2", - "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.1", - "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", - "phpunitgoodpractices/traits": "^1.8", - "symfony/phpunit-bridge": "^4.3" + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.0", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.5.3", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.16", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.6", + "phpunitgoodpractices/traits": "^1.9.2", + "symfony/phpunit-bridge": "^6.2.3", + "symfony/yaml": "^5.4 || ^6.0" }, "suggest": { - "ext-mbstring": "For handling non-UTF8 characters in cache signature.", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", - "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." }, "bin": [ "php-cs-fixer" @@ -5024,102 +8571,51 @@ "autoload": { "psr-4": { "PhpCsFixer\\": "src/" - }, - "classmap": [ - "tests/Test/AbstractFixerTestCase.php", - "tests/Test/AbstractIntegrationCaseFactory.php", - "tests/Test/AbstractIntegrationTestCase.php", - "tests/Test/Assert/AssertTokensTrait.php", - "tests/Test/IntegrationCase.php", - "tests/Test/IntegrationCaseFactory.php", - "tests/Test/IntegrationCaseFactoryInterface.php", - "tests/Test/InternalIntegrationCaseFactory.php", - "tests/TestCase.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], "description": "A tool to automatically fix PHP code style", - "time": "2019-06-01T10:32:12+00:00" - }, - { - "name": "fzaninotto/faker", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.4" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "François Zaninotto" + "url": "https://github.com/keradus", + "type": "github" } ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2018-07-12T10:23:15+00:00" + "time": "2023-02-09T21:49:13+00:00" }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.0", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "shasum": "" }, "require": { - "php": "^5.3|^7.0" + "php": "^5.3|^7.0|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -5127,14 +8623,13 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "1.3.3", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "^1.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -5144,43 +8639,50 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD" + "BSD-3-Clause" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], - "time": "2016-01-20T08:20:44+00:00" + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" }, { - "name": "hassankhan/config", - "version": "0.10.0", + "name": "itsgoingd/clockwork", + "version": "v5.1.12", "source": { "type": "git", - "url": "https://github.com/hassankhan/config.git", - "reference": "06ac500348af033f1a2e44dc357ca86282626d4a" + "url": "https://github.com/itsgoingd/clockwork.git", + "reference": "c9dbdbb1f0efd19bb80f1080ef63f1b9b1bc3b1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hassankhan/config/zipball/06ac500348af033f1a2e44dc357ca86282626d4a", - "reference": "06ac500348af033f1a2e44dc357ca86282626d4a", + "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/c9dbdbb1f0efd19bb80f1080ef63f1b9b1bc3b1b", + "reference": "c9dbdbb1f0efd19bb80f1080ef63f1b9b1bc3b1b", "shasum": "" }, "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0", - "scrutinizer/ocular": "~1.1", - "squizlabs/php_codesniffer": "~2.2" - }, - "suggest": { - "symfony/yaml": "~2.5" + "ext-json": "*", + "php": ">=5.6" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "Clockwork\\Support\\Laravel\\ClockworkServiceProvider" + ], + "aliases": { + "Clockwork": "Clockwork\\Support\\Laravel\\Facade" + } + } + }, "autoload": { "psr-4": { - "Noodlehaus\\": "src" + "Clockwork\\": "Clockwork/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5189,60 +8691,72 @@ ], "authors": [ { - "name": "Hassan Khan", - "homepage": "http://hassankhan.me/", - "role": "Developer" + "name": "itsgoingd", + "email": "itsgoingd@luzer.sk", + "homepage": "https://twitter.com/itsgoingd" } ], - "description": "Lightweight configuration file loader that supports PHP, INI, XML, JSON, and YAML files", - "homepage": "http://hassankhan.me/config/", + "description": "php dev tools in your browser", + "homepage": "https://underground.works/clockwork", "keywords": [ - "config", - "configuration", - "ini", - "json", - "microphp", - "unframework", - "xml", - "yaml", - "yml" + "Devtools", + "debugging", + "laravel", + "logging", + "lumen", + "profiling", + "slim" ], - "time": "2016-02-11T16:21:17+00:00" + "support": { + "issues": "https://github.com/itsgoingd/clockwork/issues", + "source": "https://github.com/itsgoingd/clockwork/tree/v5.1.12" + }, + "funding": [ + { + "url": "https://github.com/itsgoingd", + "type": "github" + } + ], + "time": "2022-12-13T00:04:12+00:00" }, { - "name": "justinrainbow/json-schema", - "version": "5.2.7", + "name": "laravel/sail", + "version": "v1.21.0", "source": { "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "8560d4314577199ba51bf2032f02cd1315587c23" + "url": "https://github.com/laravel/sail.git", + "reference": "758a914fc4da41f3f6ca5522c85902181b228bd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/8560d4314577199ba51bf2032f02cd1315587c23", - "reference": "8560d4314577199ba51bf2032f02cd1315587c23", + "url": "https://api.github.com/repos/laravel/sail/zipball/758a914fc4da41f3f6ca5522c85902181b228bd1", + "reference": "758a914fc4da41f3f6ca5522c85902181b228bd1", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" + "illuminate/console": "^8.0|^9.0|^10.0", + "illuminate/contracts": "^8.0|^9.0|^10.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "php": "^7.3|^8.0", + "symfony/yaml": "^6.0" }, "bin": [ - "bin/validate-json" + "bin/sail" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "1.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] } }, "autoload": { "psr-4": { - "JsonSchema\\": "src/JsonSchema/" + "Laravel\\Sail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5251,117 +8765,50 @@ ], "authors": [ { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", + "description": "Docker files for running a basic Laravel application.", "keywords": [ - "json", - "schema" + "docker", + "laravel" ], - "time": "2018-02-14T22:26:30+00:00" - }, - { - "name": "maximebf/debugbar", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07" + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e7d60937ee5f1320975ca9bc7bcdd44d500f07", - "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "^1.0", - "symfony/var-dumper": "^2.6|^3.0|^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0|^5.0" - }, - "suggest": { - "kriswallsmith/assetic": "The best way to manage assets", - "monolog/monolog": "Log using Monolog", - "predis/predis": "Redis storage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.14-dev" - } - }, - "autoload": { - "psr-4": { - "DebugBar\\": "src/DebugBar/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Maxime Bouroumeau-Fuseau", - "email": "maxime.bouroumeau@gmail.com", - "homepage": "http://maximebf.com" - }, - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "Debug bar in the browser for php application", - "homepage": "https://github.com/maximebf/php-debugbar", - "keywords": [ - "debug", - "debugbar" - ], - "time": "2017-12-15T11:13:46+00:00" + "time": "2023-02-16T19:16:27+00:00" }, { "name": "mockery/mockery", - "version": "1.2.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "100633629bf76d57430b86b7098cd6beb996a35a" + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/100633629bf76d57430b86b7098cd6beb996a35a", - "reference": "100633629bf76d57430b86b7098cd6beb996a35a", + "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "~2.0", + "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": ">=5.6.0" + "php": "^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "~5.7.10|~6.5|~7.0" + "phpunit/phpunit": "^8.5 || ^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { @@ -5399,41 +8846,46 @@ "test double", "testing" ], - "time": "2018-10-02T21:52:37+00:00" + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.5.1" + }, + "time": "2022-09-07T15:32:08+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.8.1", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5447,33 +8899,50 @@ "object", "object graph" ], - "time": "2018-06-11T23:09:50+00:00" + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" }, { "name": "nunomaduro/collision", - "version": "v2.1.1", + "version": "v7.0.5", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "b5feb0c0d92978ec7169232ce5d70d6da6b29f63" + "reference": "5c654ee5fa187cf2f4cb226d773582ec6d402a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/b5feb0c0d92978ec7169232ce5d70d6da6b29f63", - "reference": "b5feb0c0d92978ec7169232ce5d70d6da6b29f63", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/5c654ee5fa187cf2f4cb226d773582ec6d402a55", + "reference": "5c654ee5fa187cf2f4cb226d773582ec6d402a55", "shasum": "" }, "require": { - "filp/whoops": "^2.1.4", - "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*", - "php": "^7.1", - "symfony/console": "~2.8|~3.3|~4.0" + "filp/whoops": "^2.14.6", + "nunomaduro/termwind": "^1.15.1", + "php": "^8.1.0", + "symfony/console": "^6.2.5" }, "require-dev": { - "laravel/framework": "5.7.*", - "nunomaduro/larastan": "^0.3.0", - "phpstan/phpstan": "^0.10", - "phpunit/phpunit": "~7.3" + "laravel/framework": "^10.0.3", + "laravel/pint": "^1.5.0", + "laravel/sail": "^1.20.2", + "laravel/sanctum": "^3.2.1", + "laravel/tinker": "^2.8.0", + "nunomaduro/larastan": "^2.4.1", + "orchestra/testbench-core": "^8.0.1", + "pestphp/pest": "^2.0.0", + "phpunit/phpunit": "^10.0.9", + "sebastian/environment": "^6.0.0", + "spatie/laravel-ignition": "^2.0.0" }, "type": "library", "extra": { @@ -5484,6 +8953,9 @@ } }, "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], "psr-4": { "NunoMaduro\\Collision\\": "src/" } @@ -5511,32 +8983,147 @@ "php", "symfony" ], - "time": "2018-11-21T21:40:54+00:00" + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-02-19T16:25:13+00:00" }, { - "name": "phar-io/manifest", - "version": "1.0.3", + "name": "nunomaduro/larastan", + "version": "2.4.1", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "url": "https://github.com/nunomaduro/larastan.git", + "reference": "238fdbfba3aae133cdec73e99826c9b0232141f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/238fdbfba3aae133cdec73e99826c9b0232141f7", + "reference": "238fdbfba3aae133cdec73e99826c9b0232141f7", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.47.0 || ^10.0.0", + "illuminate/container": "^9.47.0 || ^10.0.0", + "illuminate/contracts": "^9.47.0 || ^10.0.0", + "illuminate/database": "^9.47.0 || ^10.0.0", + "illuminate/http": "^9.47.0 || ^10.0.0", + "illuminate/pipeline": "^9.47.0 || ^10.0.0", + "illuminate/support": "^9.47.0 || ^10.0.0", + "php": "^8.0.2", + "phpmyadmin/sql-parser": "^5.6.0", + "phpstan/phpstan": "^1.9.8" + }, + "require-dev": { + "nikic/php-parser": "^4.15.2", + "orchestra/testbench": "^7.19.0|^8.0.0", + "phpunit/phpunit": "^9.5.27" + }, + "suggest": { + "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" + }, + "type": "phpstan-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\Larastan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "larastan", + "laravel", + "package", + "php", + "static analysis" + ], + "support": { + "issues": "https://github.com/nunomaduro/larastan/issues", + "source": "https://github.com/nunomaduro/larastan/tree/2.4.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/canvural", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-02-05T12:19:17+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -5566,24 +9153,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -5613,258 +9204,38 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "php-cs-fixer/diff", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "symfony/process": "^3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "SpacePossum" - } - ], - "description": "sebastian/diff v2 backport support for PHP5.6", - "homepage": "https://github.com/PHP-CS-Fixer", - "keywords": [ - "diff" - ], - "time": "2018-02-15T16:58:55+00:00" - }, - { - "name": "php-mock/php-mock", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-mock/php-mock.git", - "reference": "22d297231118e6fd5b9db087fbe1ef866c2b95d2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock/zipball/22d297231118e6fd5b9db087fbe1ef866c2b95d2", - "reference": "22d297231118e6fd5b9db087fbe1ef866c2b95d2", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "phpunit/php-text-template": "^1" - }, - "replace": { - "malkusch/php-mock": "*" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock." - }, - "type": "library", - "autoload": { - "psr-4": { - "phpmock\\": [ - "classes/", - "tests/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "WTFPL" - ], - "authors": [ - { - "name": "Markus Malkusch", - "email": "markus@malkusch.de", - "homepage": "http://markus.malkusch.de", - "role": "Developer" - } - ], - "description": "PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.", - "homepage": "https://github.com/php-mock/php-mock", - "keywords": [ - "BDD", - "TDD", - "function", - "mock", - "stub", - "test", - "test double" - ], - "time": "2017-02-17T20:52:52+00:00" - }, - { - "name": "php-mock/php-mock-integration", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-mock/php-mock-integration.git", - "reference": "5a0d7d7755f823bc2a230cfa45058b40f9013bc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/5a0d7d7755f823bc2a230cfa45058b40f9013bc4", - "reference": "5a0d7d7755f823bc2a230cfa45058b40f9013bc4", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "php-mock/php-mock": "^2", - "phpunit/php-text-template": "^1" - }, - "require-dev": { - "phpunit/phpunit": "^4|^5" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpmock\\integration\\": "classes/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "WTFPL" - ], - "authors": [ - { - "name": "Markus Malkusch", - "email": "markus@malkusch.de", - "homepage": "http://markus.malkusch.de", - "role": "Developer" - } - ], - "description": "Integration package for PHP-Mock", - "homepage": "https://github.com/php-mock/php-mock-integration", - "keywords": [ - "BDD", - "TDD", - "function", - "mock", - "stub", - "test", - "test double" - ], - "time": "2017-02-17T21:31:34+00:00" - }, - { - "name": "php-mock/php-mock-phpunit", - "version": "2.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-mock/php-mock-phpunit.git", - "reference": "57b92e621f14c2c07a4567cd29ed4e87de0d2912" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/57b92e621f14c2c07a4567cd29ed4e87de0d2912", - "reference": "57b92e621f14c2c07a4567cd29ed4e87de0d2912", - "shasum": "" - }, - "require": { - "php": ">=7", - "php-mock/php-mock-integration": "^2", - "phpunit/phpunit": "^6 || ^7" - }, - "type": "library", - "autoload": { - "files": [ - "autoload.php" - ], - "psr-4": { - "phpmock\\phpunit\\": "classes/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "WTFPL" - ], - "authors": [ - { - "name": "Markus Malkusch", - "email": "markus@malkusch.de", - "homepage": "http://markus.malkusch.de", - "role": "Developer" - } - ], - "description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.", - "homepage": "https://github.com/php-mock/php-mock-phpunit", - "keywords": [ - "BDD", - "TDD", - "function", - "mock", - "phpunit", - "stub", - "test", - "test double" - ], - "time": "2018-10-07T14:38:37+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5886,92 +9257,48 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -5984,107 +9311,199 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2" + }, + "time": "2022-10-14T12:47:21+00:00" }, { - "name": "phpspec/prophecy", - "version": "1.8.0", + "name": "phpmyadmin/sql-parser", + "version": "5.7.0", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "url": "https://github.com/phpmyadmin/sql-parser.git", + "reference": "0f5895aab2b6002d00b6831b60983523dea30bff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/0f5895aab2b6002d00b6831b60983523dea30bff", + "reference": "0f5895aab2b6002d00b6831b60983523dea30bff", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "php": "^7.2 || ^8.0", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "phpmyadmin/motranslator": "<3.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpbench/phpbench": "^1.1", + "phpmyadmin/coding-standard": "^3.0", + "phpmyadmin/motranslator": "^4.0 || ^5.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.9.12", + "phpstan/phpstan-phpunit": "^1.3.3", + "phpunit/php-code-coverage": "*", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.11", + "zumba/json-serializer": "^3.0" }, + "suggest": { + "ext-mbstring": "For best performance", + "phpmyadmin/motranslator": "Translate messages to your favorite locale" + }, + "bin": [ + "bin/highlight-query", + "bin/lint-query", + "bin/tokenize-query" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" + "autoload": { + "psr-4": { + "PhpMyAdmin\\SqlParser\\": "src" } }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "The phpMyAdmin Team", + "email": "developers@phpmyadmin.net", + "homepage": "https://www.phpmyadmin.net/team/" } + ], + "description": "A validating SQL lexer and parser with a focus on MySQL dialect.", + "homepage": "https://github.com/phpmyadmin/sql-parser", + "keywords": [ + "analysis", + "lexer", + "parser", + "query linter", + "sql", + "sql lexer", + "sql linter", + "sql parser", + "sql syntax highlighter", + "sql tokenizer" + ], + "support": { + "issues": "https://github.com/phpmyadmin/sql-parser/issues", + "source": "https://github.com/phpmyadmin/sql-parser" + }, + "funding": [ + { + "url": "https://www.phpmyadmin.net/donate/", + "type": "other" + } + ], + "time": "2023-01-25T10:43:40+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "a2ffec7db373d8da4973d1d62add872db5cd22dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2ffec7db373d8da4973d1d62add872db5cd22dd", + "reference": "a2ffec7db373d8da4973d1d62add872db5cd22dd", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.10.2" + }, + "funding": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "url": "https://github.com/ondrejmirtes", + "type": "github" }, { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2023-02-23T14:36:46+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "6.1.4", + "version": "10.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + "reference": "bf4fbc9c13af7da12b3ea807574fb460f255daba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bf4fbc9c13af7da12b3ea807574fb460f255daba", + "reference": "bf4fbc9c13af7da12b3ea807574fb460f255daba", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "nikic/php-parser": "^4.14", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^10.0" }, "suggest": { - "ext-xdebug": "^2.6.0" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.1-dev" + "dev-main": "10.0-dev" } }, "autoload": { @@ -6099,8 +9518,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", @@ -6110,32 +9529,42 @@ "testing", "xunit" ], - "time": "2018-10-31T16:06:48+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:14:34+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/fd9329ab3368f59fe1fe808a189c51086bd4b6bd", + "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -6160,26 +9589,107 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-10T16:53:14+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -6201,32 +9711,42 @@ "keywords": [ "template" ], - "time": "2015-06-21T13:50:34+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:46+00:00" }, { "name": "phpunit/php-timer", - "version": "2.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -6250,106 +9770,62 @@ "keywords": [ "timer" ], - "time": "2018-02-01T13:07:23+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + "funding": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2018-10-30T05:52:18+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", - "version": "7.4.4", + "version": "10.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd" + "reference": "d18a18b07e7a9ad52d994b1785f9e301fc84b616" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b1be2c8530c4c29c3519a052c9fb6cee55053bbd", - "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d18a18b07e7a9ad52d994b1785f9e301fc84b616", + "reference": "d18a18b07e7a9ad52d994b1785f9e301fc84b616", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.0", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, - "require-dev": { - "ext-pdo": "*" + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.0", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.0", + "sebastian/global-state": "^6.0", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "ext-soap": "*" }, "bin": [ "phpunit" @@ -6357,10 +9833,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.4-dev" + "dev-main": "10.0-dev" } }, "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], "classmap": [ "src/" ] @@ -6372,8 +9851,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "The PHP Unit Testing framework.", @@ -6383,32 +9862,162 @@ "testing", "xunit" ], - "time": "2018-11-14T16:52:02+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.0.11" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-02-20T16:39:36+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "name": "sebastian/cli-parser", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:15+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" } }, "autoload": { @@ -6428,34 +10037,46 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", + "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", "shasum": "" }, "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -6468,6 +10089,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -6479,10 +10104,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -6492,33 +10113,43 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:07:16+00:00" }, { - "name": "sebastian/diff", - "version": "3.0.1", + "name": "sebastian/complexity", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "366541b989927187c4ca70490a35615d3fef2dce" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", - "reference": "366541b989927187c4ca70490a35615d3fef2dce", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", "shasum": "" }, "require": { - "php": "^7.1" + "nikic/php-parser": "^4.10", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^7.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -6532,12 +10163,69 @@ ], "authors": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:47+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "70dd1b20bc198da394ad542e988381b44e64e39f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/70dd1b20bc198da394ad542e988381b44e64e39f", + "reference": "70dd1b20bc198da394ad542e988381b44e64e39f", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -6548,32 +10236,45 @@ "unidiff", "unified diff" ], - "time": "2018-06-10T07:54:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:00:31+00:00" }, { "name": "sebastian/environment", - "version": "4.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f" + "reference": "b6f3694c6386c7959915a0037652e0c40f6f69cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/febd209a219cea7b56ad799b30ebbea34b71eb8f", - "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b6f3694c6386c7959915a0037652e0c40f6f69cc", + "reference": "b6f3694c6386c7959915a0037652e0c40f6f69cc", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^7.4" + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -6592,40 +10293,50 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], - "time": "2018-11-25T09:31:21+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:03:04+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -6638,6 +10349,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -6646,54 +10361,60 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:49+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "aab257c712de87b90194febd52e4d184551c2d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", + "reference": "aab257c712de87b90194febd52e4d184551c2d44", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" + "ext-dom": "*", + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -6716,34 +10437,101 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:07:38+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "3.0.3", + "name": "sebastian/lines-of-code", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", + "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "nikic/php-parser": "^4.10", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:02+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" } }, "autoload": { @@ -6763,32 +10551,42 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -6808,32 +10606,42 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -6846,44 +10654,57 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" }, { - "name": "sebastian/resource-operations", - "version": "2.0.1", + "name": "sebastian/type", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -6898,34 +10719,45 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -6946,35 +10778,44 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" }, { - "name": "seld/jsonlint", - "version": "1.7.1", + "name": "spatie/backtrace", + "version": "1.2.2", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38" + "url": "https://github.com/spatie/backtrace.git", + "reference": "7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1", + "reference": "7b34fee6c1ad45f8ee0498d17cd8ea9a076402c1", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "php": "^7.3|^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "ext-json": "*", + "phpunit/phpunit": "^9.3", + "symfony/var-dumper": "^5.1" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", "autoload": { "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" + "Spatie\\Backtrace\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -6983,46 +10824,141 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Freek Van de Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "JSON Linter", + "description": "A better backtrace", + "homepage": "https://github.com/spatie/backtrace", "keywords": [ - "json", - "linter", - "parser", - "validator" + "Backtrace", + "spatie" ], - "time": "2018-01-24T12:46:19+00:00" + "support": { + "source": "https://github.com/spatie/backtrace/tree/1.2.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2023-02-21T08:29:12+00:00" }, { - "name": "seld/phar-utils", - "version": "1.0.1", + "name": "spatie/flare-client-php", + "version": "1.3.5", "source": { "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" + "url": "https://github.com/spatie/flare-client-php.git", + "reference": "3e5dd5ac4928f3d2d036bd02de5eb83fd0ef1f42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/3e5dd5ac4928f3d2d036bd02de5eb83fd0ef1f42", + "reference": "3e5dd5ac4928f3d2d036bd02de5eb83fd0ef1f42", "shasum": "" }, "require": { - "php": ">=5.3" + "illuminate/pipeline": "^8.0|^9.0|^10.0", + "php": "^8.0", + "spatie/backtrace": "^1.2", + "symfony/http-foundation": "^5.0|^6.0", + "symfony/mime": "^5.2|^6.0", + "symfony/process": "^5.2|^6.0", + "symfony/var-dumper": "^5.2|^6.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.3.0", + "pestphp/pest": "^1.20", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/phpunit-snapshot-assertions": "^4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "1.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\FlareClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/spatie/flare-client-php", + "keywords": [ + "exception", + "flare", + "reporting", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/flare-client-php/issues", + "source": "https://github.com/spatie/flare-client-php/tree/1.3.5" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-01-23T15:58:46+00:00" + }, + { + "name": "spatie/ignition", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/ignition.git", + "reference": "2cf3833220cfe8fcf639544f8d7067b6469a00b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/ignition/zipball/2cf3833220cfe8fcf639544f8d7067b6469a00b0", + "reference": "2cf3833220cfe8fcf639544f8d7067b6469a00b0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0", + "spatie/flare-client-php": "^1.1", + "symfony/console": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "require-dev": { + "mockery/mockery": "^1.4", + "pestphp/pest": "^1.20", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "symfony/process": "^5.4|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2.x-dev" } }, "autoload": { "psr-4": { - "Seld\\PharUtils\\": "src/" + "Spatie\\Ignition\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -7031,40 +10967,142 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" } ], - "description": "PHAR file format utilities, for when PHP phars you up", + "description": "A beautiful error page for PHP applications.", + "homepage": "https://flareapp.io/ignition", "keywords": [ - "phra" + "error", + "flare", + "laravel", + "page" ], - "time": "2015-10-13T18:44:15+00:00" + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/ignition/issues", + "source": "https://github.com/spatie/ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-01-23T15:28:32+00:00" + }, + { + "name": "spatie/laravel-ignition", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ignition.git", + "reference": "70c0e2a22c5c4b691a34db8c98bd6d695660a97a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/70c0e2a22c5c4b691a34db8c98bd6d695660a97a", + "reference": "70c0e2a22c5c4b691a34db8c98bd6d695660a97a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/support": "^10.0", + "php": "^8.1", + "spatie/flare-client-php": "^1.3.5", + "spatie/ignition": "^1.4.3", + "symfony/console": "^6.2.3", + "symfony/var-dumper": "^6.2.3" + }, + "require-dev": { + "livewire/livewire": "^2.11", + "mockery/mockery": "^1.5.1", + "orchestra/testbench": "^8.0", + "pestphp/pest": "^1.22.3", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ], + "aliases": { + "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" + } + }, + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\LaravelIgnition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/laravel-ignition/issues", + "source": "https://github.com/spatie/laravel-ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-01-24T07:20:39+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981" + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd7bd6535beb1f0a0a9e3ee960666d0598546981", - "reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593", + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" @@ -7087,33 +11125,46 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "time": "2018-10-30T13:18:25+00:00" + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff" + "reference": "e8324d44f5af99ec2ccec849934a242f64458f86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/40f0e40d37c1c8a762334618dea597d64bbb75ff", - "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/e8324d44f5af99ec2ccec849934a242f64458f86", + "reference": "e8324d44f5af99ec2ccec849934a242f64458f86", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" @@ -7136,46 +11187,66 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony OptionsResolver Component", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ "config", "configuration", "options" ], - "time": "2018-09-18T12:45:12+00:00" + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/polyfill-php70", - "version": "v1.10.0", + "name": "symfony/polyfill-php81", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -7194,7 +11265,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -7202,31 +11273,44 @@ "portable", "shim" ], - "time": "2018-09-21T06:26:08+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.1.7", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5bfc064125b73ff81229e19381ce1c34d3416f4b" + "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5bfc064125b73ff81229e19381ce1c34d3416f4b", - "reference": "5bfc064125b73ff81229e19381ce1c34d3416f4b", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/00b6ac156aacffc53487c930e0ab14587a6607f6", + "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1", + "symfony/service-contracts": "^1|^2|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" @@ -7249,88 +11333,46 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Stopwatch Component", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", - "time": "2018-10-02T12:40:59+00:00" - }, - { - "name": "symfony/yaml", - "version": "v4.1.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "367e689b2fdc19965be435337b50bc8adf2746c9" + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.2.5" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/367e689b2fdc19965be435337b50bc8adf2746c9", - "reference": "367e689b2fdc19965be435337b50bc8adf2746c9", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2018-10-02T16:36:10+00:00" + "time": "2023-01-01T08:36:55+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -7350,7 +11392,17 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" } ], "aliases": [], @@ -7359,10 +11411,17 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.2", + "php": "^8.1 || ^8.2", + "ext-json": "*", "ext-mbstring": "*", + "ext-pdo": "*", "ext-pdo_mysql": "*", + "ext-posix": "*", "ext-zip": "*" }, - "platform-dev": [] + "platform-dev": [], + "platform-overrides": { + "php": "8.1.0" + }, + "plugin-api-version": "2.3.0" } diff --git a/config/activity.php b/config/activity.php new file mode 100644 index 000000000..6c492d334 --- /dev/null +++ b/config/activity.php @@ -0,0 +1,12 @@ + env('APP_ACTIVITY_PRUNE_DAYS', 90), + + // If set to true activity log entries generated by an admin user that is not also + // a part of the server in question will be hidden from the activity logs API response. + // + // Activity will still be properly tracked, just not displayed. + 'hide_admin_activity' => env('APP_ACTIVITY_HIDE_ADMIN', false), +]; diff --git a/config/app.php b/config/app.php index fccb8b888..68bb4132a 100644 --- a/config/app.php +++ b/config/app.php @@ -1,5 +1,7 @@ env('APP_REPORT_ALL_EXCEPTIONS', false), ], + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => 'file', + ], + /* |-------------------------------------------------------------------------- | Autoloaded Service Providers @@ -173,22 +192,16 @@ return [ /* * Application Service Providers... */ + Pterodactyl\Providers\ActivityLogServiceProvider::class, Pterodactyl\Providers\AppServiceProvider::class, Pterodactyl\Providers\AuthServiceProvider::class, + Pterodactyl\Providers\BackupsServiceProvider::class, Pterodactyl\Providers\BladeServiceProvider::class, Pterodactyl\Providers\EventServiceProvider::class, Pterodactyl\Providers\HashidsServiceProvider::class, Pterodactyl\Providers\RouteServiceProvider::class, - Pterodactyl\Providers\MacroServiceProvider::class, Pterodactyl\Providers\RepositoryServiceProvider::class, Pterodactyl\Providers\ViewComposerServiceProvider::class, - - /* - * Additional Dependencies - */ - Igaster\LaravelTheme\themeServiceProvider::class, - Prologue\Alerts\AlertsServiceProvider::class, - Lord\Laroute\LarouteServiceProvider::class, ], /* @@ -198,47 +211,17 @@ return [ | | This array of class aliases will be registered when this application | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. + | the aliases are "lazy" loaded, so they don't hinder performance. | */ - 'aliases' => [ - 'Alert' => Prologue\Alerts\Facades\Alert::class, - 'App' => Illuminate\Support\Facades\App::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Bus' => Illuminate\Support\Facades\Bus::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, + 'aliases' => Facade::defaultAliases()->merge([ 'Carbon' => Carbon\Carbon::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Input' => Illuminate\Support\Facades\Input::class, - 'Javascript' => Laracasts\Utilities\JavaScript\JavaScriptFacade::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Notification' => Illuminate\Support\Facades\Notification::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'Theme' => Igaster\LaravelTheme\Facades\Theme::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - ], + 'JavaScript' => Laracasts\Utilities\JavaScript\JavaScriptFacade::class, + + // Custom Facades + 'Activity' => Pterodactyl\Facades\Activity::class, + 'LogBatch' => Pterodactyl\Facades\LogBatch::class, + 'LogTarget' => Pterodactyl\Facades\LogTarget::class, + ])->toArray(), ]; diff --git a/config/auth.php b/config/auth.php index 02f4807e4..21e6be7b2 100644 --- a/config/auth.php +++ b/config/auth.php @@ -109,6 +109,20 @@ return [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, + 'throttle' => 60, ], ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | times out and the user is prompted to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => 10800, ]; diff --git a/config/backups.php b/config/backups.php new file mode 100644 index 000000000..f466ea4d5 --- /dev/null +++ b/config/backups.php @@ -0,0 +1,66 @@ + env('APP_BACKUP_DRIVER', Backup::ADAPTER_WINGS), + + // This value is used to determine the lifespan of UploadPart presigned urls that wings + // uses to upload backups to S3 storage. Value is in minutes, so this would default to an hour. + 'presigned_url_lifespan' => env('BACKUP_PRESIGNED_URL_LIFESPAN', 60), + + // This value defines the maximal size of a single part for the S3 multipart upload during backups + // The maximal part size must be given in bytes. The default value is 5GB. + // Note that 5GB is the maximum for a single part when using AWS S3. + 'max_part_size' => env('BACKUP_MAX_PART_SIZE', 5 * 1024 * 1024 * 1024), + + // The time to wait before automatically failing a backup, time is in minutes and defaults + // to 6 hours. To disable this feature, set the value to `0`. + 'prune_age' => env('BACKUP_PRUNE_AGE', 360), + + // Defines the backup creation throttle limits for users. In this default example, we allow + // a user to create two (successful or pending) backups per 10 minutes. Even if they delete + // a backup it will be included in the throttle count. + // + // Set the period to "0" to disable this throttle. The period is defined in seconds. + 'throttles' => [ + 'limit' => env('BACKUP_THROTTLE_LIMIT', 2), + 'period' => env('BACKUP_THROTTLE_PERIOD', 600), + ], + + 'disks' => [ + // There is no configuration for the local disk for Wings. That configuration + // is determined by the Daemon configuration, and not the Panel. + 'wings' => [ + 'adapter' => Backup::ADAPTER_WINGS, + ], + + // Configuration for storing backups in Amazon S3. This uses the same credentials + // specified in filesystems.php but does include some more specific settings for + // backups, notably bucket, location, and use_accelerate_endpoint. + 's3' => [ + 'adapter' => Backup::ADAPTER_AWS_S3, + + 'region' => env('AWS_DEFAULT_REGION'), + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + + // The S3 bucket to use for backups. + 'bucket' => env('AWS_BACKUPS_BUCKET'), + + // The location within the S3 bucket where backups will be stored. Backups + // are stored within a folder using the server's UUID as the name. Each + // backup for that server lives within that folder. + 'prefix' => env('AWS_BACKUPS_BUCKET') ?? '', + + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'use_accelerate_endpoint' => env('AWS_BACKUPS_USE_ACCELERATE', false), + + 'storage_class' => env('AWS_BACKUPS_STORAGE_CLASS'), + ], + ], +]; diff --git a/config/broadcasting.php b/config/broadcasting.php index 9c4c792de..81add6d89 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -10,9 +10,10 @@ return [ | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | - | Supported: "pusher", "redis", "log", "null" + | Supported: "pusher", "ably", "redis", "log", "null" | */ + 'default' => env('BROADCAST_DRIVER', 'null'), /* @@ -29,9 +30,24 @@ return [ 'connections' => [ 'pusher' => [ 'driver' => 'pusher', - 'key' => env('PUSHER_KEY'), - 'secret' => env('PUSHER_SECRET'), + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'host' => env('PUSHER_HOST', 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com') ?: 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com', + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), ], 'redis' => [ diff --git a/config/cache.php b/config/cache.php index bf429c4bf..14b00b51b 100644 --- a/config/cache.php +++ b/config/cache.php @@ -1,5 +1,7 @@ env('CACHE_DRIVER', 'file'), + 'default' => env('CACHE_DRIVER', 'redis'), /* |-------------------------------------------------------------------------- @@ -25,6 +25,9 @@ return [ | well as their drivers. You may even define multiple stores for the | same cache driver to group types of items stored in your caches. | + | Supported drivers: "apc", "array", "database", "file", + | "memcached", "redis", "dynamodb", "octane", "null" + | */ 'stores' => [ @@ -34,12 +37,14 @@ return [ 'array' => [ 'driver' => 'array', + 'serialize' => false, ], 'database' => [ 'driver' => 'database', 'table' => 'cache', 'connection' => null, + 'lock_connection' => null, ], 'file' => [ @@ -69,6 +74,7 @@ return [ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', + 'lock_connection' => 'default', ], 'sessions' => [ @@ -76,6 +82,10 @@ return [ 'table' => 'sessions', 'connection' => env('SESSION_DRIVER') === 'redis' ? 'sessions' : null, ], + + 'octane' => [ + 'driver' => 'octane', + ], ], /* @@ -83,11 +93,11 @@ return [ | Cache Key Prefix |-------------------------------------------------------------------------- | - | When utilizing a RAM based store such as APC or Memcached, there might - | be other applications utilizing the same cache. So, we'll specify a - | value to get prefixed to all our keys so we can avoid collisions. + | When utilizing the APC, database, memcached, Redis, or DynamoDB cache + | stores there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. | */ - 'prefix' => env('CACHE_PREFIX', str_slug(env('APP_NAME', 'pterodactyl'), '_') . '_cache'), + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'pterodactyl'), '_') . '_cache_'), ]; diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 000000000..bf72895e0 --- /dev/null +++ b/config/cors.php @@ -0,0 +1,57 @@ + ['/api/client', '/api/application', '/api/client/*', '/api/application/*'], + + /* + * Matches the request method. `['*']` allows all methods. + */ + 'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'], + + /* + * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com` + */ + 'allowed_origins' => explode(',', env('APP_CORS_ALLOWED_ORIGINS') ?? ''), + + /* + * Patterns that can be used with `preg_match` to match the origin. + */ + 'allowed_origins_patterns' => [], + + /* + * Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers. + */ + 'allowed_headers' => ['*'], + + /* + * Sets the Access-Control-Expose-Headers response header with these headers. + */ + 'exposed_headers' => [], + + /* + * Sets the Access-Control-Max-Age response header when > 0. + */ + 'max_age' => 0, + + /* + * Sets the Access-Control-Allow-Credentials header. + */ + 'supports_credentials' => true, +]; diff --git a/config/database.php b/config/database.php index 63b0b9cb6..1a8d9bf65 100644 --- a/config/database.php +++ b/config/database.php @@ -1,5 +1,8 @@ [ 'mysql' => [ 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), - 'unix_socket' => env('DB_SOCKET'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'panel'), 'username' => env('DB_USERNAME', 'pterodactyl'), 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => env('DB_PREFIX', ''), + 'prefix_indexes' => true, 'strict' => env('DB_STRICT_MODE', false), + 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE', 'UTC'))), + 'sslmode' => env('DB_SSLMODE', 'prefer'), + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + PDO::MYSQL_ATTR_SSL_CERT => env('MYSQL_ATTR_SSL_CERT'), + PDO::MYSQL_ATTR_SSL_KEY => env('MYSQL_ATTR_SSL_KEY'), + PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => env('MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', true), + ]) : [], ], - /* - | ------------------------------------------------------------------------- - | Test Database Connection - | ------------------------------------------------------------------------- - | - | This connection is used by the integration and HTTP tests for Pterodactyl - | development. Normal users of the Panel do not need to adjust any settings - | in here. - */ - 'testing' => [ - 'driver' => 'mysql', - 'host' => env('TESTING_DB_HOST', '127.0.0.1'), - 'port' => env('TESTING_DB_PORT', '3306'), - 'database' => env('TESTING_DB_DATABASE', 'panel_test'), - 'username' => env('TESTING_DB_USERNAME', 'pterodactyl_test'), - 'password' => env('TESTING_DB_PASSWORD', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', - 'strict' => false, + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'panel'), + 'username' => env('DB_USERNAME', 'pterodactyl'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => env('DB_PREFIX', ''), + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', ], ], @@ -93,20 +99,49 @@ return [ */ 'redis' => [ - 'client' => 'predis', + 'client' => env('REDIS_CLIENT', 'predis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'pterodactyl'), '_') . '_database_'), + ], 'default' => [ + 'scheme' => env('REDIS_SCHEME', 'tcp'), + 'path' => env('REDIS_PATH', '/run/redis/redis.sock'), 'host' => env('REDIS_HOST', 'localhost'), - 'password' => env('REDIS_PASSWORD', null), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', 6379), 'database' => env('REDIS_DATABASE', 0), + 'context' => extension_loaded('redis') && env('REDIS_CLIENT') === 'phpredis' ? [ + 'stream' => array_filter([ + 'verify_peer' => env('REDIS_VERIFY_PEER', true), + 'verify_peer_name' => env('REDIS_VERIFY_PEER_NAME', true), + 'cafile' => env('REDIS_CAFILE'), + 'local_cert' => env('REDIS_LOCAL_CERT'), + 'local_pk' => env('REDIS_LOCAL_PK'), + ]), + ] : [], ], 'sessions' => [ + 'scheme' => env('REDIS_SCHEME', 'tcp'), + 'path' => env('REDIS_PATH', '/run/redis/redis.sock'), 'host' => env('REDIS_HOST', 'localhost'), - 'password' => env('REDIS_PASSWORD', null), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', 6379), 'database' => env('REDIS_DATABASE_SESSIONS', 1), + 'context' => extension_loaded('redis') && env('REDIS_CLIENT') === 'phpredis' ? [ + 'stream' => array_filter([ + 'verify_peer' => env('REDIS_VERIFY_PEER', true), + 'verify_peer_name' => env('REDIS_VERIFY_PEER_NAME', true), + 'cafile' => env('REDIS_CAFILE'), + 'local_cert' => env('REDIS_LOCAL_CERT'), + 'local_pk' => env('REDIS_LOCAL_PK'), + ]), + ] : [], ], ], ]; diff --git a/config/debugbar.php b/config/debugbar.php deleted file mode 100644 index f1a1fd753..000000000 --- a/config/debugbar.php +++ /dev/null @@ -1,167 +0,0 @@ - null, - - /* - |-------------------------------------------------------------------------- - | Storage settings - |-------------------------------------------------------------------------- - | - | DebugBar stores data for session/ajax requests. - | You can disable this, so the debugbar stores data in headers/session, - | but this can cause problems with large data collectors. - | By default, file storage (in the storage folder) is used. Redis and PDO - | can also be used. For PDO, run the package migrations first. - | - */ - 'storage' => [ - 'enabled' => true, - 'driver' => env('DEBUGBAR_DRIVER', 'file'), // redis, file, pdo - 'path' => storage_path() . '/debugbar', // For file driver - 'connection' => null, // Leave null for default connection (Redis/PDO) - ], - - /* - |-------------------------------------------------------------------------- - | Vendors - |-------------------------------------------------------------------------- - | - | Vendor files are included by default, but can be set to false. - | This can also be set to 'js' or 'css', to only include javascript or css vendor files. - | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) - | and for js: jquery and and highlight.js - | So if you want syntax highlighting, set it to true. - | jQuery is set to not conflict with existing jQuery scripts. - | - */ - - 'include_vendors' => true, - - /* - |-------------------------------------------------------------------------- - | Capture Ajax Requests - |-------------------------------------------------------------------------- - | - | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), - | you can use this option to disable sending the data through the headers. - | - */ - - 'capture_ajax' => true, - - /* - |-------------------------------------------------------------------------- - | Clockwork integration - |-------------------------------------------------------------------------- - | - | The Debugbar can emulate the Clockwork headers, so you can use the Chrome - | Extension, without the server-side code. It uses Debugbar collectors instead. - | - */ - 'clockwork' => false, - - /* - |-------------------------------------------------------------------------- - | DataCollectors - |-------------------------------------------------------------------------- - | - | Enable/disable DataCollectors - | - */ - - 'collectors' => [ - 'phpinfo' => true, // Php version - 'messages' => true, // Messages - 'time' => true, // Time Datalogger - 'memory' => true, // Memory usage - 'exceptions' => true, // Exception displayer - 'log' => true, // Logs from Monolog (merged in messages if enabled) - 'db' => true, // Show database (PDO) queries and bindings - 'views' => true, // Views with their data - 'route' => true, // Current route information - 'laravel' => false, // Laravel version and environment - 'events' => true, // All events fired - 'default_request' => false, // Regular or special Symfony request logger - 'symfony_request' => true, // Only one can be enabled.. - 'mail' => true, // Catch mail messages - 'logs' => false, // Add the latest log messages - 'files' => false, // Show the included files - 'config' => false, // Display config settings - 'auth' => false, // Display Laravel authentication status - 'gate' => false, // Display Laravel Gate checks - 'session' => true, // Display session data - ], - - /* - |-------------------------------------------------------------------------- - | Extra options - |-------------------------------------------------------------------------- - | - | Configure some DataCollectors - | - */ - - 'options' => [ - 'auth' => [ - 'show_name' => false, // Also show the users name/email in the debugbar - ], - 'db' => [ - 'with_params' => true, // Render SQL with the parameters substituted - 'timeline' => true, // Add the queries to the timeline - 'backtrace' => true, // EXPERIMENTAL: Use a backtrace to find the origin of the query in your files. - 'explain' => [ // EXPERIMENTAL: Show EXPLAIN output on queries - 'enabled' => false, - 'types' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'], // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+ - ], - 'hints' => false, // Show hints for common mistakes - ], - 'mail' => [ - 'full_log' => false, - ], - 'views' => [ - 'data' => false, //Note: Can slow down the application, because the data can be quite large.. - ], - 'route' => [ - 'label' => true, // show complete route on bar - ], - 'logs' => [ - 'file' => null, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Inject Debugbar in Response - |-------------------------------------------------------------------------- - | - | Usually, the debugbar is added just before , by listening to the - | Response after the App is done. If you disable this, you have to add them - | in your template yourself. See http://phpdebugbar.com/docs/rendering.html - | - */ - - 'inject' => true, - - /* - |-------------------------------------------------------------------------- - | DebugBar route prefix - |-------------------------------------------------------------------------- - | - | Sometimes you want to set route prefix to be used by DebugBar to load - | its resources from. Usually the need comes from misconfigured web server or - | from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 - | - */ - 'route_prefix' => '_debugbar', -]; diff --git a/config/egg_features/eula.php b/config/egg_features/eula.php new file mode 100644 index 000000000..e5fb8727b --- /dev/null +++ b/config/egg_features/eula.php @@ -0,0 +1,16 @@ + env('FILESYSTEM_DRIVER', 'local'), - - /* - |-------------------------------------------------------------------------- - | Default Cloud Filesystem Disk - |-------------------------------------------------------------------------- - | - | Many applications store files both locally and in the cloud. For this - | reason, you may specify a default "cloud" driver here. This driver - | will be bound as the Cloud disk implementation in the container. - | - */ - - 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + 'default' => env('FILESYSTEM_DISK', 'local'), /* |-------------------------------------------------------------------------- @@ -34,15 +21,17 @@ return [ | | Here you may configure as many filesystem "disks" as you wish, and you | may even configure multiple disks of the same driver. Defaults have - | been setup for each driver as an example of the required options. + | been set up for each driver as an example of the required values. | - | Supported Drivers: "local", "ftp", "s3", "rackspace" + | Supported Drivers: "local", "ftp", "sftp", "s3" | */ + 'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), + 'throw' => false, ], 'public' => [ @@ -50,6 +39,7 @@ return [ 'root' => storage_path('app/public'), 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', + 'throw' => false, ], 's3' => [ @@ -58,6 +48,25 @@ return [ 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, ], ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], ]; diff --git a/config/hashing.php b/config/hashing.php index f45a6090b..552f2bbb4 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -10,7 +10,7 @@ return [ | passwords for your application. By default, the bcrypt algorithm is | used; however, you remain free to modify this option if you wish. | - | Supported: "bcrypt", "argon" + | Supported: "bcrypt", "argon", "argon2id" | */ @@ -43,8 +43,8 @@ return [ */ 'argon' => [ - 'memory' => 1024, - 'threads' => 2, - 'time' => 2, + 'memory' => 65536, + 'threads' => 1, + 'time' => 4, ], ]; diff --git a/config/http.php b/config/http.php new file mode 100644 index 000000000..ed54e475b --- /dev/null +++ b/config/http.php @@ -0,0 +1,21 @@ + [ + 'client_period' => 1, + 'client' => env('APP_API_CLIENT_RATELIMIT', 720), + + 'application_period' => 1, + 'application' => env('APP_API_APPLICATION_RATELIMIT', 240), + ], +]; diff --git a/config/ide-helper.php b/config/ide-helper.php deleted file mode 100644 index 9f10873f6..000000000 --- a/config/ide-helper.php +++ /dev/null @@ -1,175 +0,0 @@ - '_ide_helper', - 'format' => 'php', - - /* - |-------------------------------------------------------------------------- - | Fluent helpers - |-------------------------------------------------------------------------- - | - | Set to true to generate commonly used Fluent methods - | - */ - - 'include_fluent' => true, - - /* - |-------------------------------------------------------------------------- - | Write Model Magic methods - |-------------------------------------------------------------------------- - | - | Set to false to disable write magic methods of model - | - */ - - 'write_model_magic_where' => true, - - /* - |-------------------------------------------------------------------------- - | Helper files to include - |-------------------------------------------------------------------------- - | - | Include helper files. By default not included, but can be toggled with the - | -- helpers (-H) option. Extra helper files can be included. - | - */ - - 'include_helpers' => false, - - 'helper_files' => [ - base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php', - ], - - /* - |-------------------------------------------------------------------------- - | Model locations to include - |-------------------------------------------------------------------------- - | - | Define in which directories the ide-helper:models command should look - | for models. - | - */ - - 'model_locations' => [ - 'app/Models', - ], - - /* - |-------------------------------------------------------------------------- - | Extra classes - |-------------------------------------------------------------------------- - | - | These implementations are not really extended, but called with magic functions - | - */ - - 'extra' => [ - 'Eloquent' => ['Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'], - 'Session' => ['Illuminate\Session\Store'], - ], - - 'magic' => [ - 'Log' => [ - 'debug' => 'Monolog\Logger::addDebug', - 'info' => 'Monolog\Logger::addInfo', - 'notice' => 'Monolog\Logger::addNotice', - 'warning' => 'Monolog\Logger::addWarning', - 'error' => 'Monolog\Logger::addError', - 'critical' => 'Monolog\Logger::addCritical', - 'alert' => 'Monolog\Logger::addAlert', - 'emergency' => 'Monolog\Logger::addEmergency', - ], - ], - - /* - |-------------------------------------------------------------------------- - | Interface implementations - |-------------------------------------------------------------------------- - | - | These interfaces will be replaced with the implementing class. Some interfaces - | are detected by the helpers, others can be listed below. - | - */ - - 'interfaces' => [ - ], - - /* - |-------------------------------------------------------------------------- - | Support for custom DB types - |-------------------------------------------------------------------------- - | - | This setting allow you to map any custom database type (that you may have - | created using CREATE TYPE statement or imported using database plugin - | / extension to a Doctrine type. - | - | Each key in this array is a name of the Doctrine2 DBAL Platform. Currently valid names are: - | 'postgresql', 'db2', 'drizzle', 'mysql', 'oracle', 'sqlanywhere', 'sqlite', 'mssql' - | - | This name is returned by getName() method of the specific Doctrine/DBAL/Platforms/AbstractPlatform descendant - | - | The value of the array is an array of type mappings. Key is the name of the custom type, - | (for example, "jsonb" from Postgres 9.4) and the value is the name of the corresponding Doctrine2 type (in - | our case it is 'json_array'. Doctrine types are listed here: - | http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html - | - | So to support jsonb in your models when working with Postgres, just add the following entry to the array below: - | - | "postgresql" => array( - | "jsonb" => "json_array", - | ), - | - */ - 'custom_db_types' => [ - ], - - /* - |-------------------------------------------------------------------------- - | Support for camel cased models - |-------------------------------------------------------------------------- - | - | There are some Laravel packages (such as Eloquence) that allow for accessing - | Eloquent model properties via camel case, instead of snake case. - | - | Enabling this option will support these packages by saving all model - | properties as camel case, instead of snake case. - | - | For example, normally you would see this: - | - | * @property \Carbon\Carbon $created_at - | * @property \Carbon\Carbon $updated_at - | - | With this enabled, the properties will be this: - | - | * @property \Carbon\Carbon $createdAt - | * @property \Carbon\Carbon $updatedAt - | - | Note, it is currently an all-or-nothing option. - | - */ - 'model_camel_case_properties' => false, - - /* - |-------------------------------------------------------------------------- - | Property Casts - |-------------------------------------------------------------------------- - | - | Cast the given "real type" to the given "type". - | - */ - 'type_overrides' => [ - 'integer' => 'int', - 'boolean' => 'bool', - ], -]; diff --git a/config/laroute.php b/config/laroute.php deleted file mode 100644 index b2b4a2f30..000000000 --- a/config/laroute.php +++ /dev/null @@ -1,56 +0,0 @@ - 'public/js', - - /* - * The destination filename for the javascript file. - */ - 'filename' => 'laroute', - - /* - * The namespace for the helper functions. By default this will bind them to - * `window.laroute`. - */ - 'namespace' => 'Router', - - /* - * Generate absolute URLs - * - * Set the Application URL in config/app.php - */ - 'absolute' => false, - - /* - * The Filter Method - * - * 'all' => All routes except "'laroute' => false" - * 'only' => Only "'laroute' => true" routes - * 'force' => All routes, ignored "laroute" route parameter - */ - 'filter' => 'all', - - /* - * Controller Namespace - * - * Set here your controller namespace (see RouteServiceProvider -> $namespace) for cleaner action calls - * e.g. 'App\Http\Controllers' - */ - 'action_namespace' => '', - - /* - * The path to the template `laroute.js` file. This is the file that contains - * the ported helper Laravel url/route functions and the route data to go - * with them. - */ - 'template' => 'vendor/lord/laroute/src/templates/laroute.js', - - /* - * Appends a prefix to URLs. By default the prefix is an empty string. - * - */ - 'prefix' => '', -]; diff --git a/config/logging.php b/config/logging.php index 33b6a5d0e..b2cdb82da 100644 --- a/config/logging.php +++ b/config/logging.php @@ -1,9 +1,10 @@ env('LOG_CHANNEL', 'daily'), + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => false, + ], + /* |-------------------------------------------------------------------------- | Log Channels @@ -36,18 +53,19 @@ return [ 'stack' => [ 'driver' => 'stack', 'channels' => ['single'], + 'ignore_exceptions' => false, ], 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), 'days' => 7, ], @@ -56,12 +74,25 @@ return [ 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', 'emoji' => ':boom:', - 'level' => 'critical', + 'level' => env('LOG_LEVEL', 'critical'), + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), + ], ], 'stderr' => [ 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), 'handler' => StreamHandler::class, + 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ 'stream' => 'php://stderr', ], @@ -69,13 +100,21 @@ return [ 'syslog' => [ 'driver' => 'syslog', - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), ], 'errorlog' => [ 'driver' => 'errorlog', - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), ], ], - ]; diff --git a/config/mail.php b/config/mail.php index f47793438..71c693cff 100644 --- a/config/mail.php +++ b/config/mail.php @@ -3,45 +3,81 @@ return [ /* |-------------------------------------------------------------------------- - | Mail Driver + | Default Mailer |-------------------------------------------------------------------------- | - | Laravel supports both SMTP and PHP's "mail" function as drivers for the - | sending of e-mail. You may specify which one you're using throughout - | your application here. By default, Laravel is setup for SMTP mail. - | - | Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses", - | "sparkpost", "log", "array" + | This option controls the default mailer that is used to send any email + | messages sent by your application. Alternative mailers may be setup + | and used as needed; however, this mailer will be used by default. | */ - 'driver' => env('MAIL_DRIVER', 'smtp'), + 'default' => env('MAIL_MAILER', env('MAIL_DRIVER', 'smtp')), /* |-------------------------------------------------------------------------- - | SMTP Host Address + | Mailer Configurations |-------------------------------------------------------------------------- | - | Here you may provide the host address of the SMTP server used by your - | applications. A default option is provided that is compatible with - | the Mailgun mail service which will provide reliable deliveries. + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers to be used while + | sending an e-mail. You will specify which one you are using for your + | mailers below. You are free to add additional mailers as required. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "log", "array", "failover" | */ - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + 'port' => env('MAIL_PORT', 587), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', env('SERVER_NAME')), + ], - /* - |-------------------------------------------------------------------------- - | SMTP Host Port - |-------------------------------------------------------------------------- - | - | This is the SMTP port used by your application to deliver e-mails to - | users of the application. Like the host we have set this value to - | stay compatible with the Mailgun e-mail application by default. - | - */ + 'ses' => [ + 'transport' => 'ses', + ], - 'port' => env('MAIL_PORT', 587), + 'mailgun' => [ + 'transport' => 'mailgun', + ], + + 'postmark' => [ + 'transport' => 'postmark', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + ], /* |-------------------------------------------------------------------------- @@ -55,75 +91,10 @@ return [ */ 'from' => [ - 'address' => env('MAIL_FROM'), + 'address' => env('MAIL_FROM_ADDRESS', env('MAIL_FROM', 'hello@example.com')), 'name' => env('MAIL_FROM_NAME', 'Pterodactyl Panel'), ], - /* - |-------------------------------------------------------------------------- - | E-Mail Encryption Protocol - |-------------------------------------------------------------------------- - | - | Here you may specify the encryption protocol that should be used when - | the application send e-mail messages. A sensible default using the - | transport layer security protocol should provide great security. - | - */ - - 'encryption' => env('MAIL_ENCRYPTION', 'tls'), - - /* - |-------------------------------------------------------------------------- - | SMTP Server Username - |-------------------------------------------------------------------------- - | - | If your SMTP server requires a username for authentication, you should - | set it here. This will get used to authenticate with your server on - | connection. You may also set the "password" value below this one. - | - */ - - 'username' => env('MAIL_USERNAME'), - - /* - |-------------------------------------------------------------------------- - | SMTP Server Password - |-------------------------------------------------------------------------- - | - | Here you may set the password required by your SMTP server to send out - | messages from your application. This will be given to the server on - | connection so that the application will be able to send messages. - | - */ - - 'password' => env('MAIL_PASSWORD'), - - /* - |-------------------------------------------------------------------------- - | Sendmail System Path - |-------------------------------------------------------------------------- - | - | When using the "sendmail" driver to send e-mails, we will need to know - | the path to where Sendmail lives on this server. A default path has - | been provided here, which will work well on most of your systems. - | - */ - - 'sendmail' => '/usr/sbin/sendmail -bs', - - /* - |-------------------------------------------------------------------------- - | Mail "Pretend" - |-------------------------------------------------------------------------- - | - | When this option is enabled, e-mail will not actually be sent over the - | web and will instead be written to your application's logs files so - | you may inspect the message. This is great for local development. - | - */ - - 'pretend' => false, - /* |-------------------------------------------------------------------------- | Markdown Mail Settings @@ -134,6 +105,7 @@ return [ | of the emails. Or, you may simply stick with the Laravel defaults! | */ + 'markdown' => [ 'theme' => 'default', diff --git a/config/prologue/alerts.php b/config/prologue/alerts.php deleted file mode 100644 index 16397703a..000000000 --- a/config/prologue/alerts.php +++ /dev/null @@ -1,37 +0,0 @@ - [ - 'info', - 'warning', - 'danger', - 'success', - ], - - /* - |-------------------------------------------------------------------------- - | Session Key - |-------------------------------------------------------------------------- - | - | The session key which is used to store flashed messages into the current - | session. This can be changed if it conflicts with another key. - | - */ - - 'session_key' => 'alert_messages', -]; diff --git a/config/pterodactyl.php b/config/pterodactyl.php index 4379c753b..43c7c57be 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -10,6 +10,7 @@ return [ | setup on the panel. When set to true, configurations stored in the | database will not be applied. */ + 'load_environment_only' => (bool) env('APP_ENVIRONMENT_ONLY', false), /* @@ -21,6 +22,7 @@ return [ | author of custom services, and make upgrades easier by identifying | standard Pterodactyl shipped services. */ + 'service' => [ 'author' => env('APP_SERVICE_AUTHOR', 'unknown@unknown.com'), ], @@ -32,6 +34,7 @@ return [ | | Should login success and failure events trigger an email to the user? */ + 'auth' => [ '2fa_required' => env('APP_2FA_REQUIRED', 0), '2fa' => [ @@ -49,6 +52,7 @@ return [ | Certain pagination result counts can be configured here and will take | effect globally. */ + 'paginate' => [ 'frontend' => [ 'servers' => env('APP_PAGINATE_FRONT_SERVERS', 15), @@ -56,7 +60,6 @@ return [ 'admin' => [ 'servers' => env('APP_PAGINATE_ADMIN_SERVERS', 25), 'users' => env('APP_PAGINATE_ADMIN_USERS', 25), - 'packs' => env('APP_PAGINATE_ADMIN_PACKS', 50), ], 'api' => [ 'nodes' => env('APP_PAGINATE_API_NODES', 25), @@ -65,18 +68,6 @@ return [ ], ], - /* - |-------------------------------------------------------------------------- - | API Options - |-------------------------------------------------------------------------- - | - | Configuration options for the API. - */ - 'api' => [ - 'include_on_list' => env('API_INCLUDE_ON_LIST', false), - 'key_expire_time' => env('API_KEY_EXPIRE_TIME', 60 * 12), - ], - /* |-------------------------------------------------------------------------- | Guzzle Connections @@ -84,57 +75,10 @@ return [ | | Configure the timeout to be used for Guzzle connections here. */ + 'guzzle' => [ - 'timeout' => env('GUZZLE_TIMEOUT', 5), - 'connect_timeout' => env('GUZZLE_CONNECT_TIMEOUT', 3), - ], - - /* - |-------------------------------------------------------------------------- - | Queue Names - |-------------------------------------------------------------------------- - | - | Configure the names of queues to be used in the database. - */ - 'queues' => [ - 'low' => env('QUEUE_LOW', 'low'), - 'standard' => env('QUEUE_STANDARD', 'standard'), - 'high' => env('QUEUE_HIGH', 'high'), - ], - - /* - |-------------------------------------------------------------------------- - | Console Configuration - |-------------------------------------------------------------------------- - | - | Configure the speed at which data is rendered to the console. - */ - 'console' => [ - 'count' => env('CONSOLE_PUSH_COUNT', 10), - 'frequency' => env('CONSOLE_PUSH_FREQ', 200), - ], - - /* - |-------------------------------------------------------------------------- - | Daemon Connection Details - |-------------------------------------------------------------------------- - | - | Configuration for support of the new Golang based daemon. - */ - 'daemon' => [ - 'use_new_daemon' => (bool) env('APP_USE_NEW_DAEMON', false), - ], - - /* - |-------------------------------------------------------------------------- - | Task Timers - |-------------------------------------------------------------------------- - | - | The amount of time in minutes before performing certain actions on the system. - */ - 'tasks' => [ - 'clear_log' => env('PTERODACTYL_CLEAR_TASKLOG', 720), - 'delete_server' => env('PTERODACTYL_DELETE_MINUTES', 10), + 'timeout' => env('GUZZLE_TIMEOUT', 15), + 'connect_timeout' => env('GUZZLE_CONNECT_TIMEOUT', 5), ], /* @@ -145,6 +89,7 @@ return [ | Information for the panel to use when contacting the CDN to confirm | if panel is up to date. */ + 'cdn' => [ 'cache_time' => 60, 'url' => 'https://cdn.pterodactyl.io/releases/latest.json', @@ -157,11 +102,23 @@ return [ | | Allow clients to create their own databases. */ + 'client_features' => [ 'databases' => [ 'enabled' => env('PTERODACTYL_CLIENT_DATABASES_ENABLED', true), 'allow_random' => env('PTERODACTYL_CLIENT_DATABASES_ALLOW_RANDOM', true), ], + + 'schedules' => [ + // The total number of tasks that can exist for any given schedule at once. + 'per_schedule_task_limit' => env('PTERODACTYL_PER_SCHEDULE_TASK_LIMIT', 10), + ], + + 'allocations' => [ + 'enabled' => env('PTERODACTYL_CLIENT_ALLOCATIONS_ENABLED', false), + 'range_start' => env('PTERODACTYL_CLIENT_ALLOCATIONS_RANGE_START'), + 'range_end' => env('PTERODACTYL_CLIENT_ALLOCATIONS_RANGE_END'), + ], ], /* @@ -171,40 +128,11 @@ return [ | | This array includes the MIME filetypes that can be edited via the web. */ + 'files' => [ - 'max_edit_size' => env('PTERODACTYL_FILES_MAX_EDIT_SIZE', 50000), - 'editable' => [ - 'application/json', - 'application/javascript', - 'application/xml', - 'application/xhtml+xml', - 'inode/x-empty', - 'text/xml', - 'text/css', - 'text/html', - 'text/plain', - 'text/x-perl', - 'text/x-shellscript', - 'text/x-python', - ], + 'max_edit_size' => env('PTERODACTYL_FILES_MAX_EDIT_SIZE', 1024 * 1024 * 4), ], - /* - |-------------------------------------------------------------------------- - | JSON Response Routes - |-------------------------------------------------------------------------- - | - | You should not edit this block. These routes are ajax based routes that - | expect content to be returned in JSON format. - */ - 'json_routes' => [ - 'api/*', - 'daemon/*', - 'remote/*', - ], - - 'default_api_version' => 'application/vnd.pterodactyl.v1+json', - /* |-------------------------------------------------------------------------- | Dynamic Environment Variables @@ -218,5 +146,47 @@ return [ | | 'P_SERVER_CREATED_AT' => 'created_at' */ - 'environment_variables' => [], + + 'environment_variables' => [ + 'P_SERVER_ALLOCATION_LIMIT' => 'allocation_limit', + ], + + /* + |-------------------------------------------------------------------------- + | Asset Verification + |-------------------------------------------------------------------------- + | + | This section controls the output format for JS & CSS assets. + */ + + 'assets' => [ + 'use_hash' => env('PTERODACTYL_USE_ASSET_HASH', false), + ], + + /* + |-------------------------------------------------------------------------- + | Email Notification Settings + |-------------------------------------------------------------------------- + | + | This section controls what notifications are sent to users. + */ + + 'email' => [ + // Should an email be sent to a server owner once their server has completed it's first install process? + 'send_install_notification' => env('PTERODACTYL_SEND_INSTALL_NOTIFICATION', true), + // Should an email be sent to a server owner whenever their server is reinstalled? + 'send_reinstall_notification' => env('PTERODACTYL_SEND_REINSTALL_NOTIFICATION', true), + ], + + /* + |-------------------------------------------------------------------------- + | Telemetry Settings + |-------------------------------------------------------------------------- + | + | This section controls the telemetry sent by Pterodactyl. + */ + + 'telemetry' => [ + 'enabled' => env('PTERODACTYL_TELEMETRY_ENABLED', true), + ], ]; diff --git a/config/queue.php b/config/queue.php index 326a575dd..74da2e825 100644 --- a/config/queue.php +++ b/config/queue.php @@ -3,18 +3,16 @@ return [ /* |-------------------------------------------------------------------------- - | Default Queue Driver + | Default Queue Connection Name |-------------------------------------------------------------------------- | | Laravel's queue API supports an assortment of back-ends via a single | API, giving you convenient access to each back-end using the same - | syntax for each one. Here you may set the default queue driver. - | - | Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | syntax for every one. Here you may define a default connection. | */ - 'default' => env('QUEUE_CONNECTION', env('QUEUE_DRIVER', 'database')), + 'default' => env('QUEUE_CONNECTION', env('QUEUE_DRIVER', 'redis')), /* |-------------------------------------------------------------------------- @@ -37,22 +35,27 @@ return [ 'table' => 'jobs', 'queue' => env('QUEUE_STANDARD', 'standard'), 'retry_after' => 90, + 'after_commit' => false, ], 'sqs' => [ 'driver' => 'sqs', - 'key' => env('SQS_KEY'), - 'secret' => env('SQS_SECRET'), - 'prefix' => env('SQS_QUEUE_PREFIX'), - 'queue' => env('QUEUE_STANDARD', 'standard'), - 'region' => env('SQS_REGION', 'us-east-1'), + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', env('QUEUE_STANDARD', 'standard')), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', - 'queue' => env('QUEUE_STANDARD', 'standard'), + 'queue' => env('REDIS_QUEUE', env('QUEUE_STANDARD', 'standard')), 'retry_after' => 90, + 'block_for' => null, + 'after_commit' => false, ], ], @@ -68,7 +71,8 @@ return [ */ 'failed' => [ - 'database' => 'mysql', + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'mysql'), 'table' => 'failed_jobs', ], ]; diff --git a/config/recaptcha.php b/config/recaptcha.php index 22d739481..757e184a5 100644 --- a/config/recaptcha.php +++ b/config/recaptcha.php @@ -9,7 +9,7 @@ return [ /* * API endpoint for recaptcha checks. You should not edit this. */ - 'domain' => 'https://www.google.com/recaptcha/api/siteverify', + 'domain' => env('RECAPTCHA_DOMAIN', 'https://www.google.com/recaptcha/api/siteverify'), /* * Use a custom secret key, we use our public one by default diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 000000000..21129a888 --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,63 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Laravel\Sanctum\Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. If this value is null, personal access tokens do + | not expire. This won't tweak the lifetime of first-party sessions. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'verify_csrf_token' => Pterodactyl\Http\Middleware\VerifyCsrfToken::class, + 'encrypt_cookies' => Pterodactyl\Http\Middleware\EncryptCookies::class, + ], +]; diff --git a/config/services.php b/config/services.php index be637e501..5cc6e06e2 100644 --- a/config/services.php +++ b/config/services.php @@ -3,32 +3,30 @@ return [ /* |-------------------------------------------------------------------------- - | Third Party Service + | Third Party Services |-------------------------------------------------------------------------- | | This file is for storing the credentials for third party services such - | as Stripe, Mailgun, Mandrill, and others. This file provides a sane - | default location for this type of information, allowing packages - | to have a conventional place to find your various credentials. + | as Mailgun, Postmark, AWS and more. This file provides the de facto + | location for this type of information, allowing packages to have + | a conventional file to locate the various service credentials. | */ 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + 'scheme' => 'https', ], - 'mandrill' => [ - 'secret' => env('MANDRILL_SECRET'), + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), ], 'ses' => [ - 'key' => env('SES_KEY'), - 'secret' => env('SES_SECRET'), - 'region' => 'us-east-1', - ], - - 'sparkpost' => [ - 'secret' => env('SPARKPOST_SECRET'), + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], ]; diff --git a/config/session.php b/config/session.php index 4f3a2e6de..d8fb98a09 100644 --- a/config/session.php +++ b/config/session.php @@ -1,5 +1,7 @@ env('SESSION_DRIVER', 'database'), + 'driver' => env('SESSION_DRIVER', 'redis'), /* |-------------------------------------------------------------------------- @@ -28,7 +30,7 @@ return [ | */ - 'lifetime' => env('SESSION_LIFETIME', 10080), + 'lifetime' => env('SESSION_LIFETIME', 720), 'expire_on_close' => false, @@ -69,7 +71,7 @@ return [ | */ - 'connection' => env('SESSION_DRIVER') === 'redis' ? 'sessions' : null, + 'connection' => env('SESSION_CONNECTION'), /* |-------------------------------------------------------------------------- @@ -89,13 +91,15 @@ return [ | Session Cache Store |-------------------------------------------------------------------------- | - | When using the "apc" or "memcached" session drivers, you may specify a - | cache store that should be used for these sessions. This value must - | correspond with one of the application's configured cache stores. + | While using one of the framework's cache driven session backends you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + | Affects: "apc", "dynamodb", "memcached", "redis" | */ - 'store' => null, + 'store' => env('SESSION_STORE'), /* |-------------------------------------------------------------------------- @@ -121,7 +125,10 @@ return [ | */ - 'cookie' => env('SESSION_COOKIE', str_slug(env('APP_NAME', 'pterodactyl'), '_') . '_session'), + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'pterodactyl'), '_') . '_session' + ), /* |-------------------------------------------------------------------------- @@ -147,7 +154,7 @@ return [ | */ - 'domain' => env('SESSION_DOMAIN', null), + 'domain' => env('SESSION_DOMAIN'), /* |-------------------------------------------------------------------------- @@ -156,11 +163,11 @@ return [ | | By setting this option to true, session cookies will only be sent back | to the server if the browser has a HTTPS connection. This will keep - | the cookie from being sent to you if it can not be done securely. + | the cookie from being sent to you when it can't be done securely. | */ - 'secure' => env('SESSION_SECURE_COOKIE', false), + 'secure' => env('SESSION_SECURE_COOKIE'), /* |-------------------------------------------------------------------------- @@ -182,11 +189,11 @@ return [ | | This option determines how your cookies behave when cross-site requests | take place, and can be used to mitigate CSRF attacks. By default, we - | do not enable this as other CSRF protection services are in place. + | will set this value to "lax" since this is a secure default value. | - | Supported: "lax", "strict" + | Supported: "lax", "strict", "none", null | */ - 'same_site' => null, + 'same_site' => env('SESSION_SAMESITE_COOKIE', 'lax'), ]; diff --git a/config/themes.php b/config/themes.php deleted file mode 100644 index 55942e5c3..000000000 --- a/config/themes.php +++ /dev/null @@ -1,63 +0,0 @@ - true, - - /* - |-------------------------------------------------------------------------- - | Root path where theme Views will be located. - | Can be outside default views path e.g.: resources/themes - | Leave it null if you will put your themes in the default views folder - | (as defined in config\views.php) - |-------------------------------------------------------------------------- - */ - 'themes_path' => realpath(base_path('resources/themes')), - - /* - |-------------------------------------------------------------------------- - | Set behavior if an asset is not found in a Theme hierarchy. - | Available options: THROW_EXCEPTION | LOG_ERROR | IGNORE - |-------------------------------------------------------------------------- - */ - 'asset_not_found' => 'LOG_ERROR', - - /* - |-------------------------------------------------------------------------- - | Do we want a theme activated by default? Can be set at runtime with: - | Theme::set('theme-name'); - |-------------------------------------------------------------------------- - */ - 'default' => env('APP_THEME', 'pterodactyl'), - - /* - |-------------------------------------------------------------------------- - | Cache theme.json configuration files that are located in each theme's folder - | in order to avoid searching theme settings in the filesystem for each request - |-------------------------------------------------------------------------- - */ - 'cache' => true, - - /* - |-------------------------------------------------------------------------- - | Define available themes. Format: - | - | 'theme-name' => [ - | 'extends' => 'theme-to-extend', // optional - | 'views-path' => 'path-to-views', // defaults to: resources/views/theme-name - | 'asset-path' => 'path-to-assets', // defaults to: public/theme-name - | - | // You can add your own custom keys - | // Use Theme::getSetting('key') & Theme::setSetting('key', 'value') to access them - | 'key' => 'value', - | ], - | - |-------------------------------------------------------------------------- - */ - 'themes' => [ - 'pterodactyl' => [ - 'extends' => null, - 'views-path' => 'pterodactyl', - 'asset-path' => 'themes/pterodactyl', - ], - ], -]; diff --git a/config/trustedproxy.php b/config/trustedproxy.php index d14bf4a1d..7e0166af8 100644 --- a/config/trustedproxy.php +++ b/config/trustedproxy.php @@ -24,31 +24,5 @@ return [ * subsequently passed through. */ 'proxies' => in_array(env('TRUSTED_PROXIES', []), ['*', '**']) ? - env('TRUSTED_PROXIES') : explode(',', env('TRUSTED_PROXIES', null)), - - /* - * Or, to trust all proxies that connect - * directly to your server, uncomment this: - */ - // 'proxies' => '*', - - /* - * Or, to trust ALL proxies, including those that - * are in a chain of forwarding, uncomment this: - */ - // 'proxies' => '**', - - /* - * Default Header Names - * - * Change these if the proxy does - * not send the default header names. - * - * Note that headers such as X-Forwarded-For - * are transformed to HTTP_X_FORWARDED_FOR format. - * - * The following are Symfony defaults, found in - * \Symfony\Component\HttpFoundation\Request::$trustedHeaders - */ - 'headers' => \Illuminate\Http\Request::HEADER_X_FORWARDED_ALL, + env('TRUSTED_PROXIES') : explode(',', env('TRUSTED_PROXIES') ?? ''), ]; diff --git a/config/view.php b/config/view.php index 8796b0abc..24dea7a4a 100644 --- a/config/view.php +++ b/config/view.php @@ -27,5 +27,8 @@ return [ | */ - 'compiled' => realpath(storage_path('framework/views')), + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), ]; diff --git a/database/Factories/AllocationFactory.php b/database/Factories/AllocationFactory.php new file mode 100644 index 000000000..4a5eb70c0 --- /dev/null +++ b/database/Factories/AllocationFactory.php @@ -0,0 +1,36 @@ + $this->faker->unique()->ipv4, + 'port' => $this->faker->unique()->numberBetween(1024, 65535), + ]; + } + + /** + * Attaches the allocation to a specific server model. + */ + public function forServer(Server $server): self + { + return $this->for($server)->for($server->node); + } +} diff --git a/database/Factories/ApiKeyFactory.php b/database/Factories/ApiKeyFactory.php new file mode 100644 index 000000000..40c78ce26 --- /dev/null +++ b/database/Factories/ApiKeyFactory.php @@ -0,0 +1,36 @@ + ApiKey::TYPE_APPLICATION, + 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION), + 'token' => $token ?: $token = encrypt(Str::random(ApiKey::KEY_LENGTH)), + 'allowed_ips' => null, + 'memo' => 'Test Function Key', + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/Factories/BackupFactory.php b/database/Factories/BackupFactory.php new file mode 100644 index 000000000..4333ee34e --- /dev/null +++ b/database/Factories/BackupFactory.php @@ -0,0 +1,33 @@ + Uuid::uuid4()->toString(), + 'name' => $this->faker->sentence, + 'disk' => Backup::ADAPTER_WINGS, + 'is_successful' => true, + 'created_at' => CarbonImmutable::now(), + 'completed_at' => CarbonImmutable::now(), + ]; + } +} diff --git a/database/Factories/DatabaseFactory.php b/database/Factories/DatabaseFactory.php new file mode 100644 index 000000000..d3c6f2a57 --- /dev/null +++ b/database/Factories/DatabaseFactory.php @@ -0,0 +1,35 @@ + Str::random(10), + 'username' => Str::random(10), + 'remote' => '%', + 'password' => $password ?: encrypt('test123'), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/Factories/DatabaseHostFactory.php b/database/Factories/DatabaseHostFactory.php new file mode 100644 index 000000000..b65e856ea --- /dev/null +++ b/database/Factories/DatabaseHostFactory.php @@ -0,0 +1,31 @@ + $this->faker->colorName, + 'host' => $this->faker->unique()->ipv4, + 'port' => 3306, + 'username' => $this->faker->colorName, + 'password' => Crypt::encrypt($this->faker->word), + ]; + } +} diff --git a/database/Factories/EggFactory.php b/database/Factories/EggFactory.php new file mode 100644 index 000000000..4085b70e5 --- /dev/null +++ b/database/Factories/EggFactory.php @@ -0,0 +1,30 @@ + Uuid::uuid4()->toString(), + 'name' => $this->faker->name, + 'description' => implode(' ', $this->faker->sentences()), + 'startup' => 'java -jar test.jar', + ]; + } +} diff --git a/database/Factories/EggVariableFactory.php b/database/Factories/EggVariableFactory.php new file mode 100644 index 000000000..c2bce816b --- /dev/null +++ b/database/Factories/EggVariableFactory.php @@ -0,0 +1,57 @@ + $this->faker->unique()->firstName, + 'description' => $this->faker->sentence(), + 'env_variable' => Str::upper(Str::replaceArray(' ', ['_'], $this->faker->words(2, true))), + 'default_value' => $this->faker->colorName, + 'user_viewable' => 0, + 'user_editable' => 0, + 'rules' => 'required|string', + ]; + } + + /** + * Indicate that the egg variable is viewable. + */ + public function viewable(): static + { + return $this->state(function (array $attributes) { + return [ + 'user_viewable' => 1, + ]; + }); + } + + /** + * Indicate that the egg variable is editable. + */ + public function editable(): static + { + return $this->state(function (array $attributes) { + return [ + 'user_editable' => 1, + ]; + }); + } +} diff --git a/database/Factories/LocationFactory.php b/database/Factories/LocationFactory.php new file mode 100644 index 000000000..9e0af5dca --- /dev/null +++ b/database/Factories/LocationFactory.php @@ -0,0 +1,28 @@ + Str::random(8), + 'long' => Str::random(32), + ]; + } +} diff --git a/database/Factories/NestFactory.php b/database/Factories/NestFactory.php new file mode 100644 index 000000000..9a5755ba1 --- /dev/null +++ b/database/Factories/NestFactory.php @@ -0,0 +1,30 @@ + Uuid::uuid4()->toString(), + 'author' => 'testauthor@example.com', + 'name' => $this->faker->word, + 'description' => null, + ]; + } +} diff --git a/database/Factories/NodeFactory.php b/database/Factories/NodeFactory.php new file mode 100644 index 000000000..65786f702 --- /dev/null +++ b/database/Factories/NodeFactory.php @@ -0,0 +1,46 @@ + Uuid::uuid4()->toString(), + 'public' => true, + 'name' => 'FactoryNode_' . Str::random(10), + 'fqdn' => $this->faker->unique()->ipv4, + 'listen_port_http' => 8080, + 'listen_port_sftp' => 2022, + 'public_port_http' => 8080, + 'public_port_sftp' => 2022, + 'scheme' => 'http', + 'behind_proxy' => false, + 'memory' => 1024, + 'memory_overallocate' => 0, + 'disk' => 10240, + 'disk_overallocate' => 0, + 'upload_size' => 100, + 'daemon_token_id' => Str::random(Node::DAEMON_TOKEN_ID_LENGTH), + 'daemon_token' => Crypt::encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)), + 'daemon_base' => Node::DEFAULT_DAEMON_BASE, + ]; + } +} diff --git a/database/Factories/ScheduleFactory.php b/database/Factories/ScheduleFactory.php new file mode 100644 index 000000000..0a28f4c41 --- /dev/null +++ b/database/Factories/ScheduleFactory.php @@ -0,0 +1,26 @@ + $this->faker->firstName(), + ]; + } +} diff --git a/database/Factories/ServerFactory.php b/database/Factories/ServerFactory.php new file mode 100644 index 000000000..0e2cb07f8 --- /dev/null +++ b/database/Factories/ServerFactory.php @@ -0,0 +1,50 @@ + Uuid::uuid4()->toString(), + 'uuidShort' => Str::lower(Str::random(8)), + 'name' => $this->faker->firstName, + 'description' => implode(' ', $this->faker->sentences()), + 'skip_scripts' => 0, + 'status' => null, + 'memory' => 512, + 'swap' => 0, + 'disk' => 512, + 'io' => 500, + 'cpu' => 0, + 'threads' => null, + 'oom_killer' => true, + 'startup' => '/bin/bash echo "hello world"', + 'image' => 'foo/bar:latest', + 'allocation_limit' => null, + 'database_limit' => null, + 'backup_limit' => 0, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/Factories/SubuserFactory.php b/database/Factories/SubuserFactory.php new file mode 100644 index 000000000..7db93d4d1 --- /dev/null +++ b/database/Factories/SubuserFactory.php @@ -0,0 +1,29 @@ + [ + Permission::ACTION_WEBSOCKET_CONNECT, + ], + ]; + } +} diff --git a/database/Factories/TaskFactory.php b/database/Factories/TaskFactory.php new file mode 100644 index 000000000..32bf950cc --- /dev/null +++ b/database/Factories/TaskFactory.php @@ -0,0 +1,22 @@ + $this->faker->numberBetween(1, 10), + 'action' => 'command', + 'payload' => 'test command', + 'time_offset' => 120, + 'is_queued' => false, + ]; + } +} diff --git a/database/Factories/UserFactory.php b/database/Factories/UserFactory.php new file mode 100644 index 000000000..4ebe192c7 --- /dev/null +++ b/database/Factories/UserFactory.php @@ -0,0 +1,48 @@ + null, + 'uuid' => Uuid::uuid4()->toString(), + 'username' => $this->faker->userName . '_' . Str::random(10), + 'email' => Str::random(32) . '@example.com', + 'password' => $password ?: $password = bcrypt('password'), + 'language' => 'en', + 'root_admin' => false, + 'use_totp' => false, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } + + /** + * Indicate that the user is an admin. + */ + public function admin(): static + { + return $this->state(['root_admin' => true]); + } +} diff --git a/database/Factories/UserSSHKeyFactory.php b/database/Factories/UserSSHKeyFactory.php new file mode 100644 index 000000000..ab20b251d --- /dev/null +++ b/database/Factories/UserSSHKeyFactory.php @@ -0,0 +1,64 @@ + 'ssh-dss AAAAB3NzaC1kc3MAAACBAPfiWwEFvBOafdUmHDPjXsUttt+65FHSZSCVVeEFOTaL7Y3d0CJyrtck8KS1vmXHSb8QFBY2B1yVSb/reaQvNreWZN3KDYfLbF57/zimBn+IrHrJR+ZglhOxDRHoGPWK7q9jYIrOLwoOjkNKXxz1eOHKUgufFfSNtIRLycEXczLrAAAAFQC6LnBErezotG52jN4JostfC/TfEwAAAIACuTxRzYFDXHAxFICeqqY9w+y+v2yQfdeQ1BgCq2GMagUYfOdqnjizTO9M614r/nXZK1SV10TqhUcQtkJzDQIUtBqzBF5cIC/1cIFKzXi5rNHs8Y4bz/PBD+EbQJdiy+1so1oi790r710bqnkzTravAOJ5rGyfuQRLt+f+kuS9NAAAAIEA7tjGtJuXGUtPIXfnrMYS1iOWryO4irqnvaWfel002/DaGaNjRghNe/cUBYlAsjPhGJ1F7BQlLAY1koliTY6l0svs7ZPBM5QOumrr8OaNXGGVIq/RkkxuZHmRoUL2qH3DGYaktPUn4vFPliiAmGWOHAEu1K6B4g4vG/SKgMRpIvc=', + 'rsa_2048' => 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC4VVsHFO5MxvCtAPyoKGANWyuwZ4fvllvFog5RJbfpDpw8etDFVGEXl+uRR8p79g9oV7MscoFo6HiWrJc4/4vlP665msjosILdIcbnuzMhvXnKisaGh9zflkpyR3KhUxoHxqYp2q8XtffjKKAHz1a8o7OUG6fwaKIqu+d0PoICZQ==', + 'rsa_4096' => 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCo/YLm2SPSlOIG7AagBSmEe5c0b2PLPzUGFp3gARhD6n6ydBS40TlWzeg2qV95lh6fWBd8LsNgPOFmmuKuNZdBjAGeTY4gxKfHY1vK5/zOI4jPPqAMcCMNfd82aM97kx6dO8Hw1R79OyVpOZylpXLHayVPGHUK37Tpih4W7TeVSMrOqQF9F72lzhwgEtkdjm4gLBL6RpdNXrdnjIaNVnuade0Sb3w384vecZPe+S/997WirOMNy2JU4NdMHEnSjd1/i463RpN96AsXFAu1zl9nrXVhA7DVfSHoigXAqbs/xav8PRpLgAKjYpPohxQ9Nu6tP5jRUhfWdYwNFFp/aWloD/0JdP9LqcBBc9sO9TLkz3fBiUf11VM/QT1UhO84G+ahMxVn95jA472VPUe8uKff69lzbvSavEE6qcQX2TzVKOSi1E26Fzc6IZ/tHEuGEbGFxTsiQ1GysVZ0wr1p6ftd1SVqH5F/oaEK7UO8+xn/syEqaPf6A0eJWRNc0+lHA1sIRjmo9MOBvbkKExkx5JLHgGG81DYDFdZUuHY1BgSxJJcmNWV5BKRm350EbgRngoYI5tB3tCiZVW1PI8qyff9mBae11LY5GPlUeDnPrMvSdCKMIWrg7nC8SbndBCO3Fx4z7G2dTQy4ZmY7Ae9jR4pyg7tTOI3qgl8Z462GZi/jzw==', + 'ed25519' => 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOaXIq09NH4a93EVdrvHYiZ67Wj+GBEBQ9ou4W0qSYm2', + ]; + + /** + * Returns a fake public key for use on the system. + * + * @return array + */ + public function definition() + { + $key = PublicKeyLoader::loadPublicKey(static::$keys['ed25519']); + + return [ + 'name' => $this->faker->name(), + 'public_key' => $key->toString('PKCS8'), + 'fingerprint' => $key->getFingerprint('sha256'), + ]; + } + + /** + * Returns a DSA public key. + */ + public function dsa(): self + { + $key = PublicKeyLoader::loadPublicKey(static::$keys['dsa']); + + return $this->state([ + 'public_key' => $key->toString('PKCS8'), + 'fingerprint' => $key->getFingerprint('sha256'), + ]); + } + + /** + * Returns an RSA public key, if "weak" is specified a 1024-bit RSA key is returned + * which should fail validation when being stored. + */ + public function rsa(bool $weak = false): self + { + $key = PublicKeyLoader::loadPublicKey(static::$keys[$weak ? 'rsa_2048' : 'rsa_4096']); + + return $this->state([ + 'public_key' => $key->toString('PKCS8'), + 'fingerprint' => $key->getFingerprint('sha256'), + ]); + } +} diff --git a/app/Policies/.gitkeep b/database/Seeders/.gitkeep similarity index 100% rename from app/Policies/.gitkeep rename to database/Seeders/.gitkeep diff --git a/database/seeds/DatabaseSeeder.php b/database/Seeders/DatabaseSeeder.php similarity index 89% rename from database/seeds/DatabaseSeeder.php rename to database/Seeders/DatabaseSeeder.php index fa426deae..ae534fe6a 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/Seeders/DatabaseSeeder.php @@ -1,5 +1,7 @@ parseEggFiles( + Nest::query()->where('author', 'support@pterodactyl.io')->where('name', $nest)->firstOrFail() + ); + } + } + + /** + * Loop through the list of egg files and import them. + * + * @throws \JsonException + */ + protected function parseEggFiles(Nest $nest) + { + $files = new \DirectoryIterator(database_path('Seeders/eggs/' . kebab_case($nest->name))); + + $this->command->alert('Updating Eggs for Nest: ' . $nest->name); + /** @var \DirectoryIterator $file */ + foreach ($files as $file) { + if (!$file->isFile() || !$file->isReadable()) { + continue; + } + + $decoded = json_decode(file_get_contents($file->getRealPath()), true, 512, JSON_THROW_ON_ERROR); + $file = new UploadedFile($file->getPathname(), $file->getFilename(), 'application/json'); + + $egg = $nest->eggs() + ->where('author', $decoded['author']) + ->where('name', $decoded['name']) + ->first(); + + if ($egg instanceof Egg) { + $this->updateImporterService->handle($egg, $file); + $this->command->info('Updated ' . $decoded['name']); + } else { + $this->importerService->handleFile($nest->id, $file); + $this->command->comment('Created ' . $decoded['name']); + } + } + + $this->command->line(''); + } +} diff --git a/database/seeds/NestSeeder.php b/database/Seeders/NestSeeder.php similarity index 90% rename from database/seeds/NestSeeder.php rename to database/Seeders/NestSeeder.php index fdd775645..eae7ae21b 100644 --- a/database/seeds/NestSeeder.php +++ b/database/Seeders/NestSeeder.php @@ -1,5 +1,7 @@ Reinstall Server to apply.", + "env_variable": "VANILLA_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|between:3,15", + "field_type": "text" + } + ] +} diff --git a/database/Seeders/eggs/rust/egg-rust.json b/database/Seeders/eggs/rust/egg-rust.json new file mode 100644 index 000000000..2ec4bee07 --- /dev/null +++ b/database/Seeders/eggs/rust/egg-rust.json @@ -0,0 +1,204 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2023-03-25T13:37:00+00:00", + "name": "Rust", + "author": "support@pterodactyl.io", + "description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.", + "features": [ + "steam_disk_space" + ], + "docker_images": { + "quay.io\/pterodactyl\/core:rust": "quay.io\/pterodactyl\/core:rust" + }, + "file_denylist": [], + "startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Server startup complete\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\nSRCDS_APPID=258550\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Server Name", + "description": "The name of your server in the public server list.", + "env_variable": "HOSTNAME", + "default_value": "A Rust Server", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:60", + "field_type": "text" + }, + { + "name": "Modding Framework", + "description": "The modding framework to be used: carbon, oxide, vanilla.\r\nDefaults to \"vanilla\" for a non-modded server installation.", + "env_variable": "FRAMEWORK", + "default_value": "vanilla", + "user_viewable": true, + "user_editable": true, + "rules": "required|in:carbon,oxide,vanilla", + "field_type": "text" + }, + { + "name": "Level", + "description": "The world file for Rust to use.", + "env_variable": "LEVEL", + "default_value": "Procedural Map", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:20", + "field_type": "text" + }, + { + "name": "Description", + "description": "The description under your server title. Commonly used for rules & info. Use \\n for newlines.", + "env_variable": "DESCRIPTION", + "default_value": "Powered by Pterodactyl", + "user_viewable": true, + "user_editable": true, + "rules": "required|string", + "field_type": "text" + }, + { + "name": "URL", + "description": "The URL for your server. This is what comes up when clicking the \"Visit Website\" button.", + "env_variable": "SERVER_URL", + "default_value": "http:\/\/pterodactyl.io", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + }, + { + "name": "World Size", + "description": "The world size for a procedural map.", + "env_variable": "WORLD_SIZE", + "default_value": "3000", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "World Seed", + "description": "The seed for a procedural map.", + "env_variable": "WORLD_SEED", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string", + "field_type": "text" + }, + { + "name": "Max Players", + "description": "The maximum amount of players allowed in the server at once.", + "env_variable": "MAX_PLAYERS", + "default_value": "40", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "Server Image", + "description": "The header image for the top of your server listing.", + "env_variable": "SERVER_IMG", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + }, + { + "name": "Query Port", + "description": "Server Query Port. Can't be the same as Game's primary port.", + "env_variable": "QUERY_PORT", + "default_value": "27017", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "RCON Port", + "description": "Port for RCON connections.", + "env_variable": "RCON_PORT", + "default_value": "28016", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "RCON Password", + "description": "RCON access password.", + "env_variable": "RCON_PASS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^[\\w.-]*$\/|max:64", + "field_type": "text" + }, + { + "name": "Save Interval", + "description": "Sets the server\u2019s auto-save interval in seconds.", + "env_variable": "SAVEINTERVAL", + "default_value": "60", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "Additional Arguments", + "description": "Add additional startup parameters to the server.", + "env_variable": "ADDITIONAL_ARGS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string", + "field_type": "text" + }, + { + "name": "App Port", + "description": "Port for the Rust+ App. -1 to disable.", + "env_variable": "APP_PORT", + "default_value": "28082", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer", + "field_type": "text" + }, + { + "name": "Server Logo", + "description": "The circular server logo for the Rust+ app.", + "env_variable": "SERVER_LOGO", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + }, + { + "name": "Custom Map URL", + "description": "Overwrites the map with the one from the direct download URL. Invalid URLs will cause the server to crash.", + "env_variable": "MAP_URL", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|url", + "field_type": "text" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json b/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json new file mode 100644 index 000000000..660ebd14e --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json @@ -0,0 +1,124 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:01:38-05:00", + "name": "Ark: Survival Evolved", + "author": "dev@shepper.fr", + "description": "As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! \u2014 Gamepedia: ARK", + "features": [ + "steam_disk_space" + ], + "images": [ + "quay.io\/parkervcp\/pterodactyl-images:debian_source" + ], + "file_denylist": [], + "startup": "rmv() { echo -e \"stopping server\"; rcon -t rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c saveworld && rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c DoExit; }; trap rmv 15; cd ShooterGame\/Binaries\/Linux && .\/ShooterGameServer {{SERVER_MAP}}?listen?SessionName=\"{{SESSION_NAME}}\"?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?RCONPort={{RCON_PORT}}?QueryPort={{QUERY_PORT}}?RCONEnabled=True$( [ \"$BATTLE_EYE\" == \"1\" ] || printf %s ' -NoBattlEye' ) -server {{ARGS}} -log & until echo \"waiting for rcon connection...\"; rcon -t rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD}; do sleep 5; done", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Waiting commands for 127.0.0.1:\"\r\n}", + "logs": "{}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'ubuntu:18.04'\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\n\r\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n## create a symbolic link for loading mods\r\ncd \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\nln -sf ..\/..\/..\/..\/..\/Steam\/steamapps steamapps\r\ncd \/mnt\/server", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Server Password", + "description": "If specified, players must provide this password to join the server.", + "env_variable": "ARK_PASSWORD", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|alpha_dash|between:1,100" + }, + { + "name": "Admin Password", + "description": "If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.", + "env_variable": "ARK_ADMIN_PASSWORD", + "default_value": "PleaseChangeMe", + "user_viewable": true, + "user_editable": true, + "rules": "required|alpha_dash|between:1,100" + }, + { + "name": "Server Map", + "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction, Valguero_P, Genesis, CrystalIsles, Gen2, LostIsland, Fjordur", + "env_variable": "SERVER_MAP", + "default_value": "TheIsland", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:20" + }, + { + "name": "Server Name", + "description": "ARK server name", + "env_variable": "SESSION_NAME", + "default_value": "A Pterodactyl Hosted ARK Server", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:128" + }, + { + "name": "Rcon Port", + "description": "ARK rcon port used by rcon tools.", + "env_variable": "RCON_PORT", + "default_value": "27020", + "user_viewable": true, + "user_editable": true, + "rules": "required|numeric" + }, + { + "name": "Query Port", + "description": "ARK query port used by steam server browser and ark client server browser.", + "env_variable": "QUERY_PORT", + "default_value": "27015", + "user_viewable": true, + "user_editable": true, + "rules": "required|numeric" + }, + { + "name": "Auto-update server", + "description": "This is to enable auto-updating for servers.\r\n\r\nDefault is 0. Set to 1 to update", + "env_variable": "AUTO_UPDATE", + "default_value": "0", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" + }, + { + "name": "Battle Eye", + "description": "Enable BattleEye\r\n\r\n0 to disable\r\n1 to enable\r\n\r\ndefault=\"1\"", + "env_variable": "BATTLE_EYE", + "default_value": "1", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" + }, + { + "name": "App ID", + "description": "ARK steam app id for auto updates. Leave blank to avoid auto update.", + "env_variable": "SRCDS_APPID", + "default_value": "376030", + "user_viewable": true, + "user_editable": false, + "rules": "nullable|numeric" + }, + { + "name": "Additional Arguments", + "description": "Specify additional launch parameters such as -crossplay. You must include a dash - and separate each parameter with space: -crossplay -exclusivejoin", + "env_variable": "ARGS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json new file mode 100644 index 000000000..7c9a9779f --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json @@ -0,0 +1,62 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:01:54-05:00", + "name": "Counter-Strike: Global Offensive", + "author": "support@pterodactyl.io", + "description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.", + "features": [ + "gsl_token", + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Connection to Steam servers successful\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Map", + "description": "The default map for the server.", + "env_variable": "SRCDS_MAP", + "default_value": "de_dust2", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_dash" + }, + { + "name": "Steam Account Token", + "description": "The Steam Account Token required for the server to be displayed publicly.", + "env_variable": "STEAM_ACC", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_num|size:32" + }, + { + "name": "Source AppID", + "description": "Required for game to update on server restart. Do not modify this.", + "env_variable": "SRCDS_APPID", + "default_value": "740", + "user_viewable": false, + "user_editable": false, + "rules": "required|string|max:20" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json b/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json new file mode 100644 index 000000000..6c93175f5 --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json @@ -0,0 +1,88 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:03:08-05:00", + "name": "Custom Source Engine Game", + "author": "support@pterodactyl.io", + "description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.", + "features": [ + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Game ID", + "description": "The ID corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_APPID", + "default_value": "", + "user_viewable": true, + "user_editable": false, + "rules": "required|numeric|digits_between:1,6" + }, + { + "name": "Game Name", + "description": "The name corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_GAME", + "default_value": "", + "user_viewable": true, + "user_editable": false, + "rules": "required|alpha_dash|between:1,100" + }, + { + "name": "Map", + "description": "The default map for the server.", + "env_variable": "SRCDS_MAP", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_dash" + }, + { + "name": "Steam Username", + "description": "", + "env_variable": "STEAM_USER", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + }, + { + "name": "Steam Password", + "description": "", + "env_variable": "STEAM_PASS", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + }, + { + "name": "Steam Auth", + "description": "", + "env_variable": "STEAM_AUTH", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-garrys-mod.json b/database/Seeders/eggs/source-engine/egg-garrys-mod.json new file mode 100644 index 000000000..32829174d --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-garrys-mod.json @@ -0,0 +1,107 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:04:20-05:00", + "name": "Garrys Mod", + "author": "support@pterodactyl.io", + "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", + "features": [ + "gsl_token", + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game garrysmod -console -port {{SERVER_PORT}} +ip 0.0.0.0 +host_workshop_collection {{WORKSHOP_ID}} +map {{SRCDS_MAP}} +gamemode {{GAMEMODE}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}} +maxplayers {{MAX_PLAYERS}} -tickrate {{TICKRATE}} $( [ \"$LUA_REFRESH\" == \"1\" ] || printf %s '-disableluarefresh' )", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n# Creating needed default files for the game\r\ncd \/mnt\/server\/garrysmod\/lua\/autorun\/server\r\necho '\r\n-- Docs: https:\/\/wiki.garrysmod.com\/page\/resource\/AddWorkshop\r\n-- Place the ID of the workshop addon you want to be downloaded to people who join your server, not the collection ID\r\n-- Use https:\/\/beta.configcreator.com\/create\/gmod\/resources.lua to easily create a list based on your collection ID\r\n\r\nresource.AddWorkshop( \"\" )\r\n' > workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\nsv_downloadurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\n\/\/ sv_location \"eu\"\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Map", + "description": "The default map for the server.", + "env_variable": "SRCDS_MAP", + "default_value": "gm_flatgrass", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_dash" + }, + { + "name": "Steam Account Token", + "description": "The Steam Account Token required for the server to be displayed publicly.", + "env_variable": "STEAM_ACC", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|string|alpha_num|size:32" + }, + { + "name": "Source AppID", + "description": "Required for game to update on server restart. Do not modify this.", + "env_variable": "SRCDS_APPID", + "default_value": "4020", + "user_viewable": false, + "user_editable": false, + "rules": "required|string|max:20" + }, + { + "name": "Workshop ID", + "description": "The ID of your workshop collection (the numbers at the end of the URL)", + "env_variable": "WORKSHOP_ID", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|integer" + }, + { + "name": "Gamemode", + "description": "The gamemode of your server.", + "env_variable": "GAMEMODE", + "default_value": "sandbox", + "user_viewable": true, + "user_editable": true, + "rules": "required|string" + }, + { + "name": "Max Players", + "description": "The maximum amount of players allowed on your game server.", + "env_variable": "MAX_PLAYERS", + "default_value": "32", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer|max:128" + }, + { + "name": "Tickrate", + "description": "The tickrate defines how fast the server will update each entity's location.", + "env_variable": "TICKRATE", + "default_value": "22", + "user_viewable": true, + "user_editable": true, + "rules": "required|integer|max:100" + }, + { + "name": "Lua Refresh", + "description": "0 = disable Lua refresh,\r\n1 = enable Lua refresh", + "env_variable": "LUA_REFRESH", + "default_value": "0", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-insurgency.json b/database/Seeders/eggs/source-engine/egg-insurgency.json new file mode 100644 index 000000000..69d182f3d --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-insurgency.json @@ -0,0 +1,52 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-18T07:07:27-05:00", + "name": "Insurgency", + "author": "support@pterodactyl.io", + "description": "Take to the streets for intense close quarters combat, where a team's survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.", + "features": [ + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game insurgency -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login anonymous +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Game ID", + "description": "The ID corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_APPID", + "default_value": "237410", + "user_viewable": true, + "user_editable": false, + "rules": "required|regex:\/^(237410)$\/" + }, + { + "name": "Default Map", + "description": "The default map to use when starting the server.", + "env_variable": "SRCDS_MAP", + "default_value": "sinjar", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^(\\w{1,20})$\/" + } + ] +} diff --git a/database/Seeders/eggs/source-engine/egg-team-fortress2.json b/database/Seeders/eggs/source-engine/egg-team-fortress2.json new file mode 100644 index 000000000..6785984ed --- /dev/null +++ b/database/Seeders/eggs/source-engine/egg-team-fortress2.json @@ -0,0 +1,62 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-01-30T14:09:22-05:00", + "name": "Team Fortress 2", + "author": "support@pterodactyl.io", + "description": "Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.", + "features": [ + "gsl_token", + "steam_disk_space" + ], + "images": [ + "ghcr.io\/pterodactyl\/games:source" + ], + "file_denylist": [], + "startup": ".\/srcds_run -game tf -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", + "stop": "quit" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +force_install_dir \/mnt\/server +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "container": "ghcr.io\/pterodactyl\/installers:debian", + "entrypoint": "bash" + } + }, + "variables": [ + { + "name": "Game ID", + "description": "The ID corresponding to the game to download and run using SRCDS.", + "env_variable": "SRCDS_APPID", + "default_value": "232250", + "user_viewable": true, + "user_editable": false, + "rules": "required|regex:\/^(232250)$\/" + }, + { + "name": "Default Map", + "description": "The default map to use when starting the server.", + "env_variable": "SRCDS_MAP", + "default_value": "cp_dustbowl", + "user_viewable": true, + "user_editable": true, + "rules": "required|regex:\/^(\\w{1,20})$\/" + }, + { + "name": "Steam", + "description": "The Steam Game Server Login Token to display servers publicly. Generate one at https:\/\/steamcommunity.com\/dev\/managegameservers", + "env_variable": "STEAM_ACC", + "default_value": "", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|alpha_num|size:32" + } + ] +} diff --git a/database/Seeders/eggs/voice-servers/egg-mumble-server.json b/database/Seeders/eggs/voice-servers/egg-mumble-server.json new file mode 100644 index 000000000..feac4dc1a --- /dev/null +++ b/database/Seeders/eggs/voice-servers/egg-mumble-server.json @@ -0,0 +1,42 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v2", + "update_url": null + }, + "exported_at": "2022-10-15T12:38:18+02:00", + "name": "Mumble Server", + "author": "support@pterodactyl.io", + "description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.", + "features": null, + "docker_images": { + "Mumble": "ghcr.io\/parkervcp\/yolks:voice_mumble" + }, + "file_denylist": [], + "startup": "mumble-server -fg -ini murmur.ini", + "config": { + "files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.build.default.port}}\",\r\n \"host\": \"0.0.0.0\",\r\n \"users\": \"{{server.build.env.MAX_USERS}}\"\r\n }\r\n }\r\n}", + "startup": "{\r\n \"done\": \"Server listening on\"\r\n}", + "logs": "{}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nFILE=\/mnt\/server\/murmur.ini\r\nif [ -f \"$FILE\" ]; then\r\n echo \"Config file already exists.\"\r\nelse \r\n echo \"Downloading the config file.\"\r\n apk add --no-cache murmur\r\n cp \/etc\/murmur.ini \/mnt\/server\/murmur.ini\r\n apk del murmur\r\nfi\r\necho \"done\"", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Maximum Users", + "description": "Maximum concurrent users on the mumble server.", + "env_variable": "MAX_USERS", + "default_value": "100", + "user_viewable": true, + "user_editable": false, + "rules": "required|numeric|digits_between:1,5", + "field_type": "text" + } + ] +} diff --git a/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json new file mode 100644 index 000000000..ef15f1ca0 --- /dev/null +++ b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json @@ -0,0 +1,92 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2021-06-15T17:24:18-04:00", + "name": "Teamspeak3 Server", + "author": "support@pterodactyl.io", + "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", + "features": null, + "images": [ + "ghcr.io\/pterodactyl\/yolks:debian" + ], + "file_denylist": [], + "startup": ".\/ts3server default_voice_port={{SERVER_PORT}} query_port={{QUERY_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} query_http_port={{QUERY_HTTP}} query_ssh_port={{QUERY_SSH}} query_protocols={{QUERY_PROTOCOLS_VAR}} license_accepted=1", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"listening on 0.0.0.0:\"\r\n}", + "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/ts3.log\"\r\n}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": "#!\/bin\/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(curl -sSL https:\/\/teamspeak.com\/versions\/server.json | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\" \r\ncurl -L http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar -xvj --strip-components=1\r\ncp .\/redist\/libmariadb.so.2 .\/", + "container": "ghcr.io\/pterodactyl\/installers:alpine", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "Server Version", + "description": "The version of Teamspeak 3 to use when running the server.", + "env_variable": "TS_VERSION", + "default_value": "latest", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:6", + "field_type": "text" + }, + { + "name": "File Transfer Port", + "description": "The Teamspeak file transfer port", + "env_variable": "FILE_TRANSFER", + "default_value": "30033", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query Port", + "description": "The Teamspeak Query Port", + "env_variable": "QUERY_PORT", + "default_value": "10011", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query Protocols", + "description": "Comma separated list of protocols that can be used to connect to the ServerQuery | \r\nPossible values are raw, ssh and http | \r\nE.g.: raw,ssh,http", + "env_variable": "QUERY_PROTOCOLS_VAR", + "default_value": "raw,http,ssh", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:12", + "field_type": "text" + }, + { + "name": "Query SSH Port", + "description": "TCP Port opened for ServerQuery connections using SSH", + "env_variable": "QUERY_SSH", + "default_value": "10022", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query HTTP Port", + "description": "TCP Port opened for ServerQuery connections using http", + "env_variable": "QUERY_HTTP", + "default_value": "10080", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + } + ] +} diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php deleted file mode 100644 index 8866fa555..000000000 --- a/database/factories/ModelFactory.php +++ /dev/null @@ -1,245 +0,0 @@ -define(Pterodactyl\Models\Server::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'node_id' => $faker->randomNumber(), - 'uuid' => $faker->unique()->uuid, - 'uuidShort' => str_random(8), - 'name' => $faker->firstName, - 'description' => implode(' ', $faker->sentences()), - 'skip_scripts' => 0, - 'suspended' => 0, - 'memory' => 512, - 'swap' => 0, - 'disk' => 512, - 'io' => 500, - 'cpu' => 0, - 'oom_disabled' => 0, - 'allocation_id' => $faker->randomNumber(), - 'nest_id' => $faker->randomNumber(), - 'egg_id' => $faker->randomNumber(), - 'pack_id' => null, - 'installed' => 1, - 'database_limit' => null, - 'allocation_limit' => null, - 'created_at' => \Carbon\Carbon::now(), - 'updated_at' => \Carbon\Carbon::now(), - ]; -}); - -$factory->define(Pterodactyl\Models\User::class, function (Faker $faker) { - static $password; - - return [ - 'id' => $faker->unique()->randomNumber(), - 'external_id' => $faker->unique()->isbn10, - 'uuid' => $faker->uuid, - 'username' => $faker->userName, - 'email' => $faker->safeEmail, - 'name_first' => $faker->firstName, - 'name_last' => $faker->lastName, - 'password' => $password ?: $password = bcrypt('password'), - 'language' => 'en', - 'root_admin' => false, - 'use_totp' => false, - 'created_at' => Chronos::now(), - 'updated_at' => Chronos::now(), - ]; -}); - -$factory->state(Pterodactyl\Models\User::class, 'admin', function () { - return [ - 'root_admin' => true, - ]; -}); - -$factory->define(Pterodactyl\Models\Location::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'short' => $faker->unique()->domainWord, - 'long' => $faker->catchPhrase, - ]; -}); - -$factory->define(Pterodactyl\Models\Node::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'public' => true, - 'name' => $faker->firstName, - 'fqdn' => $faker->ipv4, - 'scheme' => 'http', - 'behind_proxy' => false, - 'memory' => 1024, - 'memory_overallocate' => 0, - 'disk' => 10240, - 'disk_overallocate' => 0, - 'upload_size' => 100, - 'daemonSecret' => $faker->uuid, - 'daemonListen' => 8080, - 'daemonSFTP' => 2022, - 'daemonBase' => '/srv/daemon', - ]; -}); - -$factory->define(Pterodactyl\Models\Nest::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'uuid' => $faker->unique()->uuid, - 'author' => 'testauthor@example.com', - 'name' => $faker->word, - 'description' => null, - ]; -}); - -$factory->define(Pterodactyl\Models\Egg::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'uuid' => $faker->unique()->uuid, - 'nest_id' => $faker->unique()->randomNumber(), - 'name' => $faker->name, - 'description' => implode(' ', $faker->sentences(3)), - 'startup' => 'java -jar test.jar', - ]; -}); - -$factory->define(Pterodactyl\Models\EggVariable::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'name' => $faker->firstName, - 'description' => $faker->sentence(), - 'env_variable' => strtoupper(str_replace(' ', '_', $faker->words(2, true))), - 'default_value' => $faker->colorName, - 'user_viewable' => 0, - 'user_editable' => 0, - 'rules' => 'required|string', - ]; -}); - -$factory->state(Pterodactyl\Models\EggVariable::class, 'viewable', function () { - return ['user_viewable' => 1]; -}); - -$factory->state(Pterodactyl\Models\EggVariable::class, 'editable', function () { - return ['user_editable' => 1]; -}); - -$factory->define(Pterodactyl\Models\Pack::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'egg_id' => $faker->randomNumber(), - 'uuid' => $faker->uuid, - 'name' => $faker->word, - 'description' => null, - 'version' => $faker->randomNumber(), - 'selectable' => 1, - 'visible' => 1, - 'locked' => 0, - ]; -}); - -$factory->define(Pterodactyl\Models\Subuser::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'user_id' => $faker->randomNumber(), - 'server_id' => $faker->randomNumber(), - ]; -}); - -$factory->define(Pterodactyl\Models\Allocation::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'node_id' => $faker->randomNumber(), - 'ip' => $faker->ipv4, - 'port' => $faker->randomNumber(5), - ]; -}); - -$factory->define(Pterodactyl\Models\DatabaseHost::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'name' => $faker->colorName, - 'host' => $faker->unique()->ipv4, - 'port' => 3306, - 'username' => $faker->colorName, - 'password' => Crypt::encrypt($faker->word), - 'node_id' => $faker->randomNumber(), - ]; -}); - -$factory->define(Pterodactyl\Models\Database::class, function (Faker $faker) { - static $password; - - return [ - 'id' => $faker->unique()->randomNumber(), - 'server_id' => $faker->randomNumber(), - 'database_host_id' => $faker->randomNumber(), - 'database' => str_random(10), - 'username' => str_random(10), - 'remote' => '%', - 'password' => $password ?: bcrypt('test123'), - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), - ]; -}); - -$factory->define(Pterodactyl\Models\Schedule::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'server_id' => $faker->randomNumber(), - 'name' => $faker->firstName(), - ]; -}); - -$factory->define(Pterodactyl\Models\Task::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'schedule_id' => $faker->randomNumber(), - 'sequence_id' => $faker->randomNumber(1), - 'action' => 'command', - 'payload' => 'test command', - 'time_offset' => 120, - 'is_queued' => false, - ]; -}); - -$factory->define(Pterodactyl\Models\DaemonKey::class, function (Faker $faker) { - return [ - 'id' => $faker->unique()->randomNumber(), - 'server_id' => $faker->randomNumber(), - 'user_id' => $faker->randomNumber(), - 'secret' => 'i_' . str_random(40), - 'expires_at' => \Carbon\Carbon::now()->addMinutes(10)->toDateTimeString(), - ]; -}); - -$factory->define(Pterodactyl\Models\ApiKey::class, function (Faker $faker) { - static $token; - - return [ - 'id' => $faker->unique()->randomNumber(), - 'user_id' => $faker->randomNumber(), - 'key_type' => ApiKey::TYPE_APPLICATION, - 'identifier' => str_random(Pterodactyl\Models\ApiKey::IDENTIFIER_LENGTH), - 'token' => $token ?: $token = encrypt(str_random(Pterodactyl\Models\ApiKey::KEY_LENGTH)), - 'allowed_ips' => null, - 'memo' => 'Test Function Key', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), - ]; -}); diff --git a/database/migrations/2016_01_23_195641_add_allocations_table.php b/database/migrations/2016_01_23_195641_add_allocations_table.php index cfff2b359..e6306c3b2 100644 --- a/database/migrations/2016_01_23_195641_add_allocations_table.php +++ b/database/migrations/2016_01_23_195641_add_allocations_table.php @@ -1,5 +1,6 @@ increments('id'); @@ -23,7 +24,7 @@ class AddAllocationsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('allocations'); } diff --git a/database/migrations/2016_01_23_195851_add_api_keys.php b/database/migrations/2016_01_23_195851_add_api_keys.php index af7deb62d..1a7824b1c 100644 --- a/database/migrations/2016_01_23_195851_add_api_keys.php +++ b/database/migrations/2016_01_23_195851_add_api_keys.php @@ -1,5 +1,6 @@ increments('id'); @@ -22,7 +23,7 @@ class AddApiKeys extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('api_keys'); } diff --git a/database/migrations/2016_01_23_200044_add_api_permissions.php b/database/migrations/2016_01_23_200044_add_api_permissions.php index e6f6bcbf8..e587da0a3 100644 --- a/database/migrations/2016_01_23_200044_add_api_permissions.php +++ b/database/migrations/2016_01_23_200044_add_api_permissions.php @@ -1,5 +1,6 @@ increments('id'); @@ -20,7 +21,7 @@ class AddApiPermissions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('api_permissions'); } diff --git a/database/migrations/2016_01_23_200159_add_downloads.php b/database/migrations/2016_01_23_200159_add_downloads.php index b1771c5e4..9424578fb 100644 --- a/database/migrations/2016_01_23_200159_add_downloads.php +++ b/database/migrations/2016_01_23_200159_add_downloads.php @@ -1,5 +1,6 @@ increments('id'); @@ -22,7 +23,7 @@ class AddDownloads extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('downloads'); } diff --git a/database/migrations/2016_01_23_200421_create_failed_jobs_table.php b/database/migrations/2016_01_23_200421_create_failed_jobs_table.php index 83923e7d0..50d42ccc9 100644 --- a/database/migrations/2016_01_23_200421_create_failed_jobs_table.php +++ b/database/migrations/2016_01_23_200421_create_failed_jobs_table.php @@ -1,5 +1,6 @@ increments('id'); @@ -22,7 +23,7 @@ class CreateFailedJobsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('failed_jobs'); } diff --git a/database/migrations/2016_01_23_200440_create_jobs_table.php b/database/migrations/2016_01_23_200440_create_jobs_table.php index 277acae31..fe7f9686c 100644 --- a/database/migrations/2016_01_23_200440_create_jobs_table.php +++ b/database/migrations/2016_01_23_200440_create_jobs_table.php @@ -1,5 +1,6 @@ bigIncrements('id'); @@ -19,6 +20,7 @@ class CreateJobsTable extends Migration $table->unsignedInteger('reserved_at')->nullable(); $table->unsignedInteger('available_at'); $table->unsignedInteger('created_at'); + $table->index(['queue', 'reserved', 'reserved_at']); }); } @@ -26,7 +28,7 @@ class CreateJobsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('jobs'); } diff --git a/database/migrations/2016_01_23_200528_add_locations.php b/database/migrations/2016_01_23_200528_add_locations.php index b34a5fbcc..38d1e1710 100644 --- a/database/migrations/2016_01_23_200528_add_locations.php +++ b/database/migrations/2016_01_23_200528_add_locations.php @@ -1,5 +1,6 @@ increments('id'); @@ -21,7 +22,7 @@ class AddLocations extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('locations'); } diff --git a/database/migrations/2016_01_23_200648_add_nodes.php b/database/migrations/2016_01_23_200648_add_nodes.php index 52c0a29e6..371ebf049 100644 --- a/database/migrations/2016_01_23_200648_add_nodes.php +++ b/database/migrations/2016_01_23_200648_add_nodes.php @@ -1,5 +1,6 @@ increments('id'); @@ -23,7 +24,7 @@ class AddNodes extends Migration $table->mediumInteger('disk_overallocate')->unsigned()->nullable(); $table->char('daemonSecret', 36)->unique(); $table->smallInteger('daemonListen')->unsigned()->default(8080); - $table->smallInteger('daemonSFTP')->unsgined()->default(2022); + $table->smallInteger('daemonSFTP')->unsigned()->default(2022); $table->string('daemonBase')->default('/home/daemon-files'); $table->timestamps(); }); @@ -32,7 +33,7 @@ class AddNodes extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('nodes'); } diff --git a/database/migrations/2016_01_23_201433_add_password_resets.php b/database/migrations/2016_01_23_201433_add_password_resets.php index 0584e3617..47c49146d 100644 --- a/database/migrations/2016_01_23_201433_add_password_resets.php +++ b/database/migrations/2016_01_23_201433_add_password_resets.php @@ -1,5 +1,6 @@ string('email')->index(); @@ -20,7 +21,7 @@ class AddPasswordResets extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('password_resets'); } diff --git a/database/migrations/2016_01_23_201531_add_permissions.php b/database/migrations/2016_01_23_201531_add_permissions.php index 12c9bbe0f..120a0e034 100644 --- a/database/migrations/2016_01_23_201531_add_permissions.php +++ b/database/migrations/2016_01_23_201531_add_permissions.php @@ -1,5 +1,6 @@ increments('id'); @@ -22,7 +23,7 @@ class AddPermissions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('permissions'); } diff --git a/database/migrations/2016_01_23_201649_add_server_variables.php b/database/migrations/2016_01_23_201649_add_server_variables.php index d9a436e6d..596c619d0 100644 --- a/database/migrations/2016_01_23_201649_add_server_variables.php +++ b/database/migrations/2016_01_23_201649_add_server_variables.php @@ -1,5 +1,6 @@ increments('id'); @@ -22,7 +23,7 @@ class AddServerVariables extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('server_variables'); } diff --git a/database/migrations/2016_01_23_201748_add_servers.php b/database/migrations/2016_01_23_201748_add_servers.php index 5e1061069..901c1ff5c 100644 --- a/database/migrations/2016_01_23_201748_add_servers.php +++ b/database/migrations/2016_01_23_201748_add_servers.php @@ -1,5 +1,6 @@ increments('id'); @@ -39,7 +40,7 @@ class AddServers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('servers'); } diff --git a/database/migrations/2016_01_23_202544_add_service_options.php b/database/migrations/2016_01_23_202544_add_service_options.php index 7b0a33609..382f67a1a 100644 --- a/database/migrations/2016_01_23_202544_add_service_options.php +++ b/database/migrations/2016_01_23_202544_add_service_options.php @@ -1,5 +1,6 @@ increments('id'); @@ -24,7 +25,7 @@ class AddServiceOptions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('service_options'); } diff --git a/database/migrations/2016_01_23_202731_add_service_varibles.php b/database/migrations/2016_01_23_202731_add_service_varibles.php index e79fa1fe9..bb96d83b6 100644 --- a/database/migrations/2016_01_23_202731_add_service_varibles.php +++ b/database/migrations/2016_01_23_202731_add_service_varibles.php @@ -1,5 +1,6 @@ increments('id'); @@ -28,7 +29,7 @@ class AddServiceVaribles extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('service_variables'); } diff --git a/database/migrations/2016_01_23_202943_add_services.php b/database/migrations/2016_01_23_202943_add_services.php index 31f723445..caddd964b 100644 --- a/database/migrations/2016_01_23_202943_add_services.php +++ b/database/migrations/2016_01_23_202943_add_services.php @@ -1,5 +1,6 @@ increments('id'); @@ -24,7 +25,7 @@ class AddServices extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('services'); } diff --git a/database/migrations/2016_01_23_203119_create_settings_table.php b/database/migrations/2016_01_23_203119_create_settings_table.php index 2cd6922c2..40dec55c6 100644 --- a/database/migrations/2016_01_23_203119_create_settings_table.php +++ b/database/migrations/2016_01_23_203119_create_settings_table.php @@ -1,5 +1,6 @@ string('key')->unique(); @@ -19,7 +20,7 @@ class CreateSettingsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('settings'); } diff --git a/database/migrations/2016_01_23_203150_add_subusers.php b/database/migrations/2016_01_23_203150_add_subusers.php index 2f0e46310..e7561c0fd 100644 --- a/database/migrations/2016_01_23_203150_add_subusers.php +++ b/database/migrations/2016_01_23_203150_add_subusers.php @@ -1,5 +1,6 @@ increments('id'); @@ -22,7 +23,7 @@ class AddSubusers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('subusers'); } diff --git a/database/migrations/2016_01_23_203159_add_users.php b/database/migrations/2016_01_23_203159_add_users.php index 05ace7e22..1b6f9b5bb 100644 --- a/database/migrations/2016_01_23_203159_add_users.php +++ b/database/migrations/2016_01_23_203159_add_users.php @@ -1,5 +1,6 @@ increments('id'); @@ -27,7 +28,7 @@ class AddUsers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('users'); } diff --git a/database/migrations/2016_01_23_203947_create_sessions_table.php b/database/migrations/2016_01_23_203947_create_sessions_table.php index 533fa8aa2..7f708195e 100644 --- a/database/migrations/2016_01_23_203947_create_sessions_table.php +++ b/database/migrations/2016_01_23_203947_create_sessions_table.php @@ -1,5 +1,6 @@ string('id')->unique(); @@ -23,7 +24,7 @@ class CreateSessionsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('sessions'); } diff --git a/database/migrations/2016_01_25_234418_rename_permissions_column.php b/database/migrations/2016_01_25_234418_rename_permissions_column.php index ae46dceb2..6b75986f9 100644 --- a/database/migrations/2016_01_25_234418_rename_permissions_column.php +++ b/database/migrations/2016_01_25_234418_rename_permissions_column.php @@ -1,5 +1,6 @@ renameColumn('permissions', 'permission'); @@ -18,7 +19,7 @@ class RenamePermissionsColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('permissions', function (Blueprint $table) { }); diff --git a/database/migrations/2016_02_07_172148_add_databases_tables.php b/database/migrations/2016_02_07_172148_add_databases_tables.php index 7b1048b15..26fdbf389 100644 --- a/database/migrations/2016_02_07_172148_add_databases_tables.php +++ b/database/migrations/2016_02_07_172148_add_databases_tables.php @@ -1,5 +1,6 @@ increments('id'); @@ -25,7 +26,7 @@ class AddDatabasesTables extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('databases'); } diff --git a/database/migrations/2016_02_07_181319_add_database_servers_table.php b/database/migrations/2016_02_07_181319_add_database_servers_table.php index 5a6740ae6..16d2d3cf5 100644 --- a/database/migrations/2016_02_07_181319_add_database_servers_table.php +++ b/database/migrations/2016_02_07_181319_add_database_servers_table.php @@ -1,5 +1,6 @@ increments('id'); @@ -26,7 +27,7 @@ class AddDatabaseServersTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('database_servers'); } diff --git a/database/migrations/2016_02_13_154306_add_service_option_default_startup.php b/database/migrations/2016_02_13_154306_add_service_option_default_startup.php index c8255ff47..a5d14b6d5 100644 --- a/database/migrations/2016_02_13_154306_add_service_option_default_startup.php +++ b/database/migrations/2016_02_13_154306_add_service_option_default_startup.php @@ -1,5 +1,6 @@ text('executable')->after('docker_image')->nullable()->default(null); @@ -19,7 +20,7 @@ class AddServiceOptionDefaultStartup extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropColumn('executable'); diff --git a/database/migrations/2016_02_20_155318_add_unique_service_field.php b/database/migrations/2016_02_20_155318_add_unique_service_field.php index 01ff91359..241e278ff 100644 --- a/database/migrations/2016_02_20_155318_add_unique_service_field.php +++ b/database/migrations/2016_02_20_155318_add_unique_service_field.php @@ -1,5 +1,6 @@ string('file')->unique()->change(); @@ -18,10 +19,10 @@ class AddUniqueServiceField extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('services', function (Blueprint $table) { - $table->dropUnique('services_file_unique'); + $table->dropUnique(['file']); }); } } diff --git a/database/migrations/2016_02_27_163411_add_tasks_table.php b/database/migrations/2016_02_27_163411_add_tasks_table.php index f4cb7b1e3..8fb1efb4a 100644 --- a/database/migrations/2016_02_27_163411_add_tasks_table.php +++ b/database/migrations/2016_02_27_163411_add_tasks_table.php @@ -1,5 +1,6 @@ increments('id'); @@ -32,7 +33,7 @@ class AddTasksTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('tasks'); } diff --git a/database/migrations/2016_02_27_163447_add_tasks_log_table.php b/database/migrations/2016_02_27_163447_add_tasks_log_table.php index 265e7fd96..6014a69b8 100644 --- a/database/migrations/2016_02_27_163447_add_tasks_log_table.php +++ b/database/migrations/2016_02_27_163447_add_tasks_log_table.php @@ -1,5 +1,6 @@ increments('id'); @@ -23,7 +24,7 @@ class AddTasksLogTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('tasks_log'); } diff --git a/database/migrations/2016_03_18_155649_add_nullable_field_lastrun.php b/database/migrations/2016_03_18_155649_add_nullable_field_lastrun.php deleted file mode 100644 index 9d4752eb6..000000000 --- a/database/migrations/2016_03_18_155649_add_nullable_field_lastrun.php +++ /dev/null @@ -1,24 +0,0 @@ -wrapTable('tasks'); - DB::statement('ALTER TABLE ' . $table . ' CHANGE `last_run` `last_run` TIMESTAMP NULL;'); - } - - /** - * Reverse the migrations. - */ - public function down() - { - $table = DB::getQueryGrammar()->wrapTable('tasks'); - DB::statement('ALTER TABLE ' . $table . ' CHANGE `last_run` `last_run` TIMESTAMP;'); - } -} diff --git a/database/migrations/2016_08_30_212718_add_ip_alias.php b/database/migrations/2016_08_30_212718_add_ip_alias.php index 26aa5eaa5..17272a2cc 100644 --- a/database/migrations/2016_08_30_212718_add_ip_alias.php +++ b/database/migrations/2016_08_30_212718_add_ip_alias.php @@ -1,5 +1,6 @@ text('ip_alias')->nullable()->after('ip'); @@ -29,7 +30,7 @@ class AddIpAlias extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('allocations', function (Blueprint $table) { $table->dropColumn('ip_alias'); diff --git a/database/migrations/2016_08_30_213301_modify_ip_storage_method.php b/database/migrations/2016_08_30_213301_modify_ip_storage_method.php index ee7e704fb..7c8b1d46b 100644 --- a/database/migrations/2016_08_30_213301_modify_ip_storage_method.php +++ b/database/migrations/2016_08_30_213301_modify_ip_storage_method.php @@ -1,5 +1,6 @@ mediumInteger('allocation')->unsigned()->after('oom_disabled'); @@ -47,7 +48,7 @@ class ModifyIpStorageMethod extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->text('ip')->after('allocation'); diff --git a/database/migrations/2016_09_01_193520_add_suspension_for_servers.php b/database/migrations/2016_09_01_193520_add_suspension_for_servers.php index 7bfb75b20..19cd96522 100644 --- a/database/migrations/2016_09_01_193520_add_suspension_for_servers.php +++ b/database/migrations/2016_09_01_193520_add_suspension_for_servers.php @@ -1,5 +1,6 @@ tinyInteger('suspended')->unsigned()->default(0)->after('active'); @@ -18,7 +19,7 @@ class AddSuspensionForServers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('suspended'); diff --git a/database/migrations/2016_09_01_211924_remove_active_column.php b/database/migrations/2016_09_01_211924_remove_active_column.php index 22a2bde13..7450c932d 100644 --- a/database/migrations/2016_09_01_211924_remove_active_column.php +++ b/database/migrations/2016_09_01_211924_remove_active_column.php @@ -1,5 +1,6 @@ dropColumn('active'); @@ -18,7 +19,7 @@ class RemoveActiveColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->tinyInteger('active')->after('name')->unsigned()->default(0); diff --git a/database/migrations/2016_09_02_190647_add_sftp_password_storage.php b/database/migrations/2016_09_02_190647_add_sftp_password_storage.php index 565957d59..57ce1f3b5 100644 --- a/database/migrations/2016_09_02_190647_add_sftp_password_storage.php +++ b/database/migrations/2016_09_02_190647_add_sftp_password_storage.php @@ -1,5 +1,6 @@ text('sftp_password')->after('username')->nullable(); @@ -18,7 +19,7 @@ class AddSftpPasswordStorage extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('sftp_password'); diff --git a/database/migrations/2016_09_04_171338_update_jobs_tables.php b/database/migrations/2016_09_04_171338_update_jobs_tables.php index 840ecacb5..4c5bff23f 100644 --- a/database/migrations/2016_09_04_171338_update_jobs_tables.php +++ b/database/migrations/2016_09_04_171338_update_jobs_tables.php @@ -9,11 +9,12 @@ class UpdateJobsTables extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('jobs', function (Blueprint $table) { - $table->dropIndex('jobs_queue_reserved_reserved_at_index'); + $table->dropIndex(['queue', 'reserved', 'reserved_at']); $table->dropColumn('reserved'); + $table->index(['queue', 'reserved_at']); }); } @@ -21,10 +22,11 @@ class UpdateJobsTables extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('jobs', function (Blueprint $table) { - $table->dropIndex('jobs_queue_reserved_at_index'); + $table->dropIndex(['queue', 'reserved_at']); + $table->tinyInteger('reserved')->unsigned(); $table->index(['queue', 'reserved', 'reserved_at']); }); diff --git a/database/migrations/2016_09_04_172028_update_failed_jobs_table.php b/database/migrations/2016_09_04_172028_update_failed_jobs_table.php index a00f5f18d..b5157a1ef 100644 --- a/database/migrations/2016_09_04_172028_update_failed_jobs_table.php +++ b/database/migrations/2016_09_04_172028_update_failed_jobs_table.php @@ -9,7 +9,7 @@ class UpdateFailedJobsTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('failed_jobs', function (Blueprint $table) { $table->text('exception'); @@ -19,7 +19,7 @@ class UpdateFailedJobsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('failed_jobs', function (Blueprint $table) { $table->dropColumn('exception'); diff --git a/database/migrations/2016_09_04_182835_create_notifications_table.php b/database/migrations/2016_09_04_182835_create_notifications_table.php index 30fc23a59..8918f3009 100644 --- a/database/migrations/2016_09_04_182835_create_notifications_table.php +++ b/database/migrations/2016_09_04_182835_create_notifications_table.php @@ -1,5 +1,6 @@ string('id')->primary(); @@ -23,7 +24,7 @@ class CreateNotificationsTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('notifications'); } diff --git a/database/migrations/2016_09_07_163017_add_unique_identifier.php b/database/migrations/2016_09_07_163017_add_unique_identifier.php index e1bab9ccc..685a718a4 100644 --- a/database/migrations/2016_09_07_163017_add_unique_identifier.php +++ b/database/migrations/2016_09_07_163017_add_unique_identifier.php @@ -9,7 +9,7 @@ class AddUniqueIdentifier extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('services', function (Blueprint $table) { $table->char('author', 36)->after('id'); @@ -19,7 +19,7 @@ class AddUniqueIdentifier extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('services', function (Blueprint $table) { $table->dropColumn('author'); diff --git a/database/migrations/2016_09_14_145945_allow_longer_regex_field.php b/database/migrations/2016_09_14_145945_allow_longer_regex_field.php index a7df1ca1b..8d0ab04ac 100644 --- a/database/migrations/2016_09_14_145945_allow_longer_regex_field.php +++ b/database/migrations/2016_09_14_145945_allow_longer_regex_field.php @@ -9,7 +9,7 @@ class AllowLongerRegexField extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_variables', function (Blueprint $table) { $table->text('regex')->change(); @@ -19,7 +19,7 @@ class AllowLongerRegexField extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_variables', function (Blueprint $table) { $table->string('regex')->change(); diff --git a/database/migrations/2016_09_17_194246_add_docker_image_column.php b/database/migrations/2016_09_17_194246_add_docker_image_column.php index 05d26112e..0e66f649e 100644 --- a/database/migrations/2016_09_17_194246_add_docker_image_column.php +++ b/database/migrations/2016_09_17_194246_add_docker_image_column.php @@ -1,5 +1,6 @@ string('image')->after('daemonSecret'); @@ -32,7 +33,7 @@ class AddDockerImageColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('image'); diff --git a/database/migrations/2016_09_21_165554_update_servers_column_name.php b/database/migrations/2016_09_21_165554_update_servers_column_name.php index 14ae07c4a..919cdcaab 100644 --- a/database/migrations/2016_09_21_165554_update_servers_column_name.php +++ b/database/migrations/2016_09_21_165554_update_servers_column_name.php @@ -9,7 +9,7 @@ class UpdateServersColumnName extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('databases', function (Blueprint $table) { $table->renameColumn('server', 'server_id'); @@ -19,7 +19,7 @@ class UpdateServersColumnName extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('databases', function (Blueprint $table) { $table->renameColumn('server_id', 'server'); diff --git a/database/migrations/2016_09_29_213518_rename_double_insurgency.php b/database/migrations/2016_09_29_213518_rename_double_insurgency.php index adb577754..5f21c7036 100644 --- a/database/migrations/2016_09_29_213518_rename_double_insurgency.php +++ b/database/migrations/2016_09_29_213518_rename_double_insurgency.php @@ -7,7 +7,7 @@ class RenameDoubleInsurgency extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::transaction(function () { $model = DB::table('service_options')->where('parent_service', 2)->where('id', 3)->where('name', 'Insurgency')->first(); @@ -21,7 +21,7 @@ class RenameDoubleInsurgency extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { } } diff --git a/database/migrations/2016_10_07_152117_build_api_log_table.php b/database/migrations/2016_10_07_152117_build_api_log_table.php index 08ea312dc..39356c5ba 100644 --- a/database/migrations/2016_10_07_152117_build_api_log_table.php +++ b/database/migrations/2016_10_07_152117_build_api_log_table.php @@ -9,7 +9,7 @@ class BuildApiLogTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::create('api_logs', function (Blueprint $table) { $table->increments('id'); @@ -28,7 +28,7 @@ class BuildApiLogTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('api_logs'); } diff --git a/database/migrations/2016_10_14_164802_update_api_keys.php b/database/migrations/2016_10_14_164802_update_api_keys.php index 56c3e8097..b43eef1b1 100644 --- a/database/migrations/2016_10_14_164802_update_api_keys.php +++ b/database/migrations/2016_10_14_164802_update_api_keys.php @@ -9,7 +9,7 @@ class UpdateApiKeys extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('api_keys', function (Blueprint $table) { $table->unsignedInteger('user')->after('id'); @@ -21,7 +21,7 @@ class UpdateApiKeys extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('api_keys', function (Blueprint $table) { $table->dropColumn('user'); diff --git a/database/migrations/2016_10_23_181719_update_misnamed_bungee.php b/database/migrations/2016_10_23_181719_update_misnamed_bungee.php index 70ec18b33..a9cf3a35c 100644 --- a/database/migrations/2016_10_23_181719_update_misnamed_bungee.php +++ b/database/migrations/2016_10_23_181719_update_misnamed_bungee.php @@ -7,7 +7,7 @@ class UpdateMisnamedBungee extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::table('service_variables')->select('env_variable')->where('env_variable', 'BUNGE_VERSION')->update([ 'env_variable' => 'BUNGEE_VERSION', @@ -17,7 +17,7 @@ class UpdateMisnamedBungee extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { } } diff --git a/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php b/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php index 1412720c9..da0fc7c83 100644 --- a/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php +++ b/database/migrations/2016_10_23_193810_add_foreign_keys_servers.php @@ -9,22 +9,21 @@ class AddForeignKeysServers extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::statement('ALTER TABLE servers - MODIFY COLUMN node INT(10) UNSIGNED NOT NULL, - MODIFY COLUMN owner INT(10) UNSIGNED NOT NULL, - MODIFY COLUMN allocation INT(10) UNSIGNED NOT NULL, - MODIFY COLUMN service INT(10) UNSIGNED NOT NULL, - MODIFY COLUMN `option` INT(10) UNSIGNED NOT NULL - '); - Schema::table('servers', function (Blueprint $table) { + $table->integer('node', false, true)->change(); + $table->integer('owner', false, true)->change(); + $table->integer('allocation', false, true)->change(); + $table->integer('service', false, true)->change(); + $table->integer('option', false, true)->change(); + $table->foreign('node')->references('id')->on('nodes'); $table->foreign('owner')->references('id')->on('users'); $table->foreign('allocation')->references('id')->on('allocations'); $table->foreign('service')->references('id')->on('services'); $table->foreign('option')->references('id')->on('service_options'); + $table->softDeletes(); }); } @@ -32,30 +31,31 @@ class AddForeignKeysServers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { - $table->dropForeign('servers_node_foreign'); - $table->dropForeign('servers_owner_foreign'); - $table->dropForeign('servers_allocation_foreign'); - $table->dropForeign('servers_service_foreign'); - $table->dropForeign('servers_option_foreign'); + $table->dropForeign(['node']); + $table->dropIndex(['node']); - $table->dropIndex('servers_node_foreign'); - $table->dropIndex('servers_owner_foreign'); - $table->dropIndex('servers_allocation_foreign'); - $table->dropIndex('servers_service_foreign'); - $table->dropIndex('servers_option_foreign'); + $table->dropForeign(['owner']); + $table->dropIndex(['owner']); + + $table->dropForeign(['allocation']); + $table->dropIndex(['allocation']); + + $table->dropForeign(['service']); + $table->dropIndex(['service']); + + $table->dropForeign(['option']); + $table->dropIndex(['option']); $table->dropColumn('deleted_at'); - }); - DB::statement('ALTER TABLE servers - MODIFY COLUMN node MEDIUMINT(8) UNSIGNED NOT NULL, - MODIFY COLUMN owner MEDIUMINT(8) UNSIGNED NOT NULL, - MODIFY COLUMN allocation MEDIUMINT(8) UNSIGNED NOT NULL, - MODIFY COLUMN service MEDIUMINT(8) UNSIGNED NOT NULL, - MODIFY COLUMN `option` MEDIUMINT(8) UNSIGNED NOT NULL - '); + $table->mediumInteger('node', false, true)->change(); + $table->mediumInteger('owner', false, true)->change(); + $table->mediumInteger('allocation', false, true)->change(); + $table->mediumInteger('service', false, true)->change(); + $table->mediumInteger('option', false, true)->change(); + }); } } diff --git a/database/migrations/2016_10_23_201624_add_foreign_allocations.php b/database/migrations/2016_10_23_201624_add_foreign_allocations.php index 0660081cb..7ae4b040d 100644 --- a/database/migrations/2016_10_23_201624_add_foreign_allocations.php +++ b/database/migrations/2016_10_23_201624_add_foreign_allocations.php @@ -1,5 +1,6 @@ integer('assigned_to', false, true)->nullable()->change(); + $table->integer('node', false, true)->nullable(false)->change(); $table->foreign('assigned_to')->references('id')->on('servers'); $table->foreign('node')->references('id')->on('nodes'); }); @@ -25,14 +23,17 @@ class AddForeignAllocations extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('allocations', function (Blueprint $table) { - $table->dropForeign('allocations_assigned_to_foreign'); - $table->dropForeign('allocations_node_foreign'); + $table->dropForeign(['assigned_to']); + $table->dropIndex(['assigned_to']); - $table->dropIndex('allocations_assigned_to_foreign'); - $table->dropIndex('allocations_node_foreign'); + $table->dropForeign(['node']); + $table->dropIndex(['node']); + + $table->mediumInteger('assigned_to', false, true)->nullable()->change(); + $table->mediumInteger('node', false, true)->nullable(false)->change(); }); DB::statement('ALTER TABLE allocations diff --git a/database/migrations/2016_10_23_202222_add_foreign_api_keys.php b/database/migrations/2016_10_23_202222_add_foreign_api_keys.php index 700342d74..44b11d0e5 100644 --- a/database/migrations/2016_10_23_202222_add_foreign_api_keys.php +++ b/database/migrations/2016_10_23_202222_add_foreign_api_keys.php @@ -9,7 +9,7 @@ class AddForeignApiKeys extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('api_keys', function (Blueprint $table) { $table->foreign('user')->references('id')->on('users'); @@ -19,11 +19,11 @@ class AddForeignApiKeys extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('api_keys', function (Blueprint $table) { - $table->dropForeign('api_keys_user_foreign'); - $table->dropIndex('api_keys_user_foreign'); + $table->dropForeign(['user']); + $table->dropIndex(['user']); }); } } diff --git a/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php b/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php index d8eb3504d..2494eaba7 100644 --- a/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php +++ b/database/migrations/2016_10_23_202703_add_foreign_api_permissions.php @@ -9,11 +9,10 @@ class AddForeignApiPermissions extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::statement('ALTER TABLE api_permissions MODIFY key_id INT(10) UNSIGNED NOT NULL'); - Schema::table('api_permissions', function (Blueprint $table) { + $table->integer('key_id', false, true)->nullable(false)->change(); $table->foreign('key_id')->references('id')->on('api_keys'); }); } @@ -21,13 +20,13 @@ class AddForeignApiPermissions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('api_permissions', function (Blueprint $table) { - $table->dropForeign('api_permissions_key_id_foreign'); - $table->dropIndex('api_permissions_key_id_foreign'); - }); + $table->dropForeign(['key_id']); + $table->dropIndex(['key_id']); - DB::statement('ALTER TABLE api_permissions MODIFY key_id MEDIUMINT(8) UNSIGNED NOT NULL'); + $table->mediumInteger('key_id', false, true)->nullable(false)->change(); + }); } } diff --git a/database/migrations/2016_10_23_202953_add_foreign_database_servers.php b/database/migrations/2016_10_23_202953_add_foreign_database_servers.php index 769b7daa3..78ee8264d 100644 --- a/database/migrations/2016_10_23_202953_add_foreign_database_servers.php +++ b/database/migrations/2016_10_23_202953_add_foreign_database_servers.php @@ -9,7 +9,7 @@ class AddForeignDatabaseServers extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('database_servers', function (Blueprint $table) { $table->foreign('linked_node')->references('id')->on('nodes'); @@ -19,11 +19,11 @@ class AddForeignDatabaseServers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('database_servers', function (Blueprint $table) { - $table->dropForeign('database_servers_linked_node_foreign'); - $table->dropIndex('database_servers_linked_node_foreign'); + $table->dropForeign(['linked_node']); + $table->dropIndex(['linked_node']); }); } } diff --git a/database/migrations/2016_10_23_203105_add_foreign_databases.php b/database/migrations/2016_10_23_203105_add_foreign_databases.php index be26e3cb0..bea43049b 100644 --- a/database/migrations/2016_10_23_203105_add_foreign_databases.php +++ b/database/migrations/2016_10_23_203105_add_foreign_databases.php @@ -9,7 +9,7 @@ class AddForeignDatabases extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('databases', function (Blueprint $table) { $table->foreign('server_id')->references('id')->on('servers'); @@ -20,14 +20,14 @@ class AddForeignDatabases extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('databases', function (Blueprint $table) { - $table->dropForeign('databases_server_id_foreign'); - $table->dropForeign('databases_db_server_foreign'); + $table->dropForeign(['server_id']); + $table->dropIndex(['server_id']); - $table->dropIndex('databases_server_id_foreign'); - $table->dropIndex('databases_db_server_foreign'); + $table->dropForeign(['db_server']); + $table->dropIndex(['db_server']); }); } } diff --git a/database/migrations/2016_10_23_203335_add_foreign_nodes.php b/database/migrations/2016_10_23_203335_add_foreign_nodes.php index f861e0a7d..375189a7f 100644 --- a/database/migrations/2016_10_23_203335_add_foreign_nodes.php +++ b/database/migrations/2016_10_23_203335_add_foreign_nodes.php @@ -9,11 +9,10 @@ class AddForeignNodes extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::statement('ALTER TABLE nodes MODIFY location INT(10) UNSIGNED NOT NULL'); - Schema::table('nodes', function (Blueprint $table) { + $table->integer('location', false, true)->nullable(false)->change(); $table->foreign('location')->references('id')->on('locations'); }); } @@ -21,13 +20,13 @@ class AddForeignNodes extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { - $table->dropForeign('nodes_location_foreign'); - $table->dropIndex('nodes_location_foreign'); - }); + $table->dropForeign(['location']); + $table->dropIndex(['location']); - DB::statement('ALTER TABLE nodes MODIFY location MEDIUMINT(10) UNSIGNED NOT NULL'); + $table->mediumInteger('location', false, true)->nullable(false)->change(); + }); } } diff --git a/database/migrations/2016_10_23_203522_add_foreign_permissions.php b/database/migrations/2016_10_23_203522_add_foreign_permissions.php index a43f0eacf..78bbf32a5 100644 --- a/database/migrations/2016_10_23_203522_add_foreign_permissions.php +++ b/database/migrations/2016_10_23_203522_add_foreign_permissions.php @@ -9,7 +9,7 @@ class AddForeignPermissions extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('permissions', function (Blueprint $table) { $table->foreign('user_id')->references('id')->on('users'); @@ -20,14 +20,14 @@ class AddForeignPermissions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('permissions', function (Blueprint $table) { - $table->dropForeign('permissions_user_id_foreign'); - $table->dropForeign('permissions_server_id_foreign'); + $table->dropForeign(['user_id']); + $table->dropIndex(['user_id']); - $table->dropIndex('permissions_user_id_foreign'); - $table->dropIndex('permissions_server_id_foreign'); + $table->dropForeign(['server_id']); + $table->dropIndex(['server_id']); }); } } diff --git a/database/migrations/2016_10_23_203857_add_foreign_server_variables.php b/database/migrations/2016_10_23_203857_add_foreign_server_variables.php index b4720495d..3ccc3d183 100644 --- a/database/migrations/2016_10_23_203857_add_foreign_server_variables.php +++ b/database/migrations/2016_10_23_203857_add_foreign_server_variables.php @@ -9,14 +9,11 @@ class AddForeignServerVariables extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::statement('ALTER TABLE server_variables - MODIFY COLUMN server_id INT(10) UNSIGNED NULL, - MODIFY COLUMN variable_id INT(10) UNSIGNED NOT NULL - '); - Schema::table('server_variables', function (Blueprint $table) { + $table->integer('server_id', false, true)->nullable()->change(); + $table->integer('variable_id', false, true)->nullable(false)->change(); $table->foreign('server_id')->references('id')->on('servers'); $table->foreign('variable_id')->references('id')->on('service_variables'); }); @@ -25,16 +22,13 @@ class AddForeignServerVariables extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('server_variables', function (Blueprint $table) { $table->dropForeign(['server_id']); $table->dropForeign(['variable_id']); + $table->mediumInteger('server_id', false, true)->nullable()->change(); + $table->mediumInteger('variable_id', false, true)->nullable(false)->change(); }); - - DB::statement('ALTER TABLE server_variables - MODIFY COLUMN server_id MEDIUMINT(8) UNSIGNED NULL, - MODIFY COLUMN variable_id MEDIUMINT(8) UNSIGNED NOT NULL - '); } } diff --git a/database/migrations/2016_10_23_204157_add_foreign_service_options.php b/database/migrations/2016_10_23_204157_add_foreign_service_options.php index cb8c0e2e8..9f01905b7 100644 --- a/database/migrations/2016_10_23_204157_add_foreign_service_options.php +++ b/database/migrations/2016_10_23_204157_add_foreign_service_options.php @@ -9,11 +9,10 @@ class AddForeignServiceOptions extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::statement('ALTER TABLE service_options MODIFY parent_service INT(10) UNSIGNED NOT NULL'); - Schema::table('service_options', function (Blueprint $table) { + $table->integer('parent_service', false, true)->change(); $table->foreign('parent_service')->references('id')->on('services'); }); } @@ -21,13 +20,13 @@ class AddForeignServiceOptions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { - $table->dropForeign('service_options_parent_service_foreign'); - $table->dropIndex('service_options_parent_service_foreign'); - }); + $table->dropForeign(['parent_service']); + $table->dropIndex(['parent_service']); - DB::statement('ALTER TABLE service_options MODIFY parent_service MEDIUMINT(8) UNSIGNED NOT NULL'); + $table->mediumInteger('parent_service', false, true)->change(); + }); } } diff --git a/database/migrations/2016_10_23_204321_add_foreign_service_variables.php b/database/migrations/2016_10_23_204321_add_foreign_service_variables.php index 02bbc46f2..df998efaf 100644 --- a/database/migrations/2016_10_23_204321_add_foreign_service_variables.php +++ b/database/migrations/2016_10_23_204321_add_foreign_service_variables.php @@ -9,11 +9,10 @@ class AddForeignServiceVariables extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::statement('ALTER TABLE service_variables MODIFY option_id INT(10) UNSIGNED NOT NULL'); - Schema::table('service_variables', function (Blueprint $table) { + $table->integer('option_id', false, true)->change(); $table->foreign('option_id')->references('id')->on('service_options'); }); } @@ -21,13 +20,13 @@ class AddForeignServiceVariables extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_variables', function (Blueprint $table) { - $table->dropForeign('service_variables_option_id_foreign'); - $table->dropIndex('service_variables_option_id_foreign'); - }); + $table->dropForeign(['option_id']); + $table->dropIndex(['option_id']); - DB::statement('ALTER TABLE service_variables MODIFY option_id MEDIUMINT(8) UNSIGNED NOT NULL'); + $table->mediumInteger('option_id', false, true)->change(); + }); } } diff --git a/database/migrations/2016_10_23_204454_add_foreign_subusers.php b/database/migrations/2016_10_23_204454_add_foreign_subusers.php index b637c80ae..ff4bb95a3 100644 --- a/database/migrations/2016_10_23_204454_add_foreign_subusers.php +++ b/database/migrations/2016_10_23_204454_add_foreign_subusers.php @@ -9,7 +9,7 @@ class AddForeignSubusers extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('subusers', function (Blueprint $table) { $table->foreign('user_id')->references('id')->on('users'); @@ -20,14 +20,14 @@ class AddForeignSubusers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('subusers', function (Blueprint $table) { - $table->dropForeign('subusers_user_id_foreign'); - $table->dropForeign('subusers_server_id_foreign'); + $table->dropForeign(['user_id']); + $table->dropIndex(['user_id']); - $table->dropIndex('subusers_user_id_foreign'); - $table->dropIndex('subusers_server_id_foreign'); + $table->dropForeign(['server_id']); + $table->dropIndex(['server_id']); }); } } diff --git a/database/migrations/2016_10_23_204610_add_foreign_tasks.php b/database/migrations/2016_10_23_204610_add_foreign_tasks.php index 18ea297e5..f32d89230 100644 --- a/database/migrations/2016_10_23_204610_add_foreign_tasks.php +++ b/database/migrations/2016_10_23_204610_add_foreign_tasks.php @@ -9,7 +9,7 @@ class AddForeignTasks extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('tasks', function (Blueprint $table) { $table->foreign('server')->references('id')->on('servers'); @@ -19,7 +19,7 @@ class AddForeignTasks extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('tasks', function (Blueprint $table) { $table->dropForeign(['server']); diff --git a/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php b/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php index 4383c11cd..c5fff5523 100644 --- a/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php +++ b/database/migrations/2016_11_04_000949_add_ark_service_option_fixed.php @@ -7,13 +7,13 @@ class AddArkServiceOptionFixed extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::transaction(function () { $service = DB::table('services')->select('id')->where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('name', 'Source Engine')->first(); // No SRCDS Service, Skipping - if (! $service) { + if (!$service) { return; } @@ -73,7 +73,7 @@ class AddArkServiceOptionFixed extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { DB::transaction(function () { $service = DB::table('services')->select('id')->where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('name', 'Source Engine')->first(); diff --git a/database/migrations/2016_11_11_220649_add_pack_support.php b/database/migrations/2016_11_11_220649_add_pack_support.php index b6fa0972b..8fd638ae6 100644 --- a/database/migrations/2016_11_11_220649_add_pack_support.php +++ b/database/migrations/2016_11_11_220649_add_pack_support.php @@ -9,7 +9,7 @@ class AddPackSupport extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::create('service_packs', function (Blueprint $table) { $table->increments('id'); @@ -29,7 +29,7 @@ class AddPackSupport extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::drop('service_packs'); } diff --git a/database/migrations/2016_11_11_231731_set_service_name_unique.php b/database/migrations/2016_11_11_231731_set_service_name_unique.php index 42b0f6953..261fdb356 100644 --- a/database/migrations/2016_11_11_231731_set_service_name_unique.php +++ b/database/migrations/2016_11_11_231731_set_service_name_unique.php @@ -9,7 +9,7 @@ class SetServiceNameUnique extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('services', function (Blueprint $table) { $table->unique('name'); @@ -19,7 +19,7 @@ class SetServiceNameUnique extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('services', function (Blueprint $table) { $table->dropUnique('services_name_unique'); diff --git a/database/migrations/2016_11_27_142519_add_pack_column.php b/database/migrations/2016_11_27_142519_add_pack_column.php index d520466a8..3911ecb41 100644 --- a/database/migrations/2016_11_27_142519_add_pack_column.php +++ b/database/migrations/2016_11_27_142519_add_pack_column.php @@ -9,7 +9,7 @@ class AddPackColumn extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->unsignedInteger('pack')->nullable()->after('option'); @@ -21,7 +21,7 @@ class AddPackColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropForeign(['pack']); diff --git a/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php b/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php index d2d14f4d0..c5136fe9e 100644 --- a/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php +++ b/database/migrations/2016_12_01_173018_add_configurable_upload_limit.php @@ -9,7 +9,7 @@ class AddConfigurableUploadLimit extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('nodes', function (Blueprint $table) { $table->unsignedInteger('upload_size')->after('disk_overallocate')->default(100); @@ -19,7 +19,7 @@ class AddConfigurableUploadLimit extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { $table->dropColumn('upload_size'); diff --git a/database/migrations/2016_12_02_185206_correct_service_variables.php b/database/migrations/2016_12_02_185206_correct_service_variables.php index e9c87989a..d94b3b78b 100644 --- a/database/migrations/2016_12_02_185206_correct_service_variables.php +++ b/database/migrations/2016_12_02_185206_correct_service_variables.php @@ -7,7 +7,7 @@ class CorrectServiceVariables extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::transaction(function () { // Modify Default Spigot Startup Line @@ -66,7 +66,7 @@ class CorrectServiceVariables extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { // do nothing } diff --git a/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php b/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php index 7cdf96807..35248d6bb 100644 --- a/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php +++ b/database/migrations/2017_01_03_150436_fix_misnamed_option_tag.php @@ -7,7 +7,7 @@ class FixMisnamedOptionTag extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::transaction(function () { DB::table('service_options')->where([ @@ -23,7 +23,7 @@ class FixMisnamedOptionTag extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { DB::table('service_options')->where([ ['name', 'Sponge (SpongeVanilla)'], diff --git a/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php b/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php index 77693c265..c4369f975 100644 --- a/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php +++ b/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php @@ -9,7 +9,7 @@ class CreateNodeConfigurationTokensTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::create('node_configuration_tokens', function (Blueprint $table) { $table->increments('id'); @@ -24,7 +24,7 @@ class CreateNodeConfigurationTokensTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('node_configuration_tokens'); } diff --git a/database/migrations/2017_01_12_135449_add_more_user_data.php b/database/migrations/2017_01_12_135449_add_more_user_data.php index 0206040b5..82ae8c9e9 100644 --- a/database/migrations/2017_01_12_135449_add_more_user_data.php +++ b/database/migrations/2017_01_12_135449_add_more_user_data.php @@ -10,7 +10,7 @@ class AddMoreUserData extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('name_first')->after('email')->nullable(); @@ -34,7 +34,7 @@ class AddMoreUserData extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('name_first'); diff --git a/database/migrations/2017_02_02_175548_UpdateColumnNames.php b/database/migrations/2017_02_02_175548_UpdateColumnNames.php index c88aa8de7..719513313 100644 --- a/database/migrations/2017_02_02_175548_UpdateColumnNames.php +++ b/database/migrations/2017_02_02_175548_UpdateColumnNames.php @@ -9,22 +9,15 @@ class UpdateColumnNames extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { - $table->dropForeign('servers_node_foreign'); - $table->dropForeign('servers_owner_foreign'); - $table->dropForeign('servers_allocation_foreign'); - $table->dropForeign('servers_service_foreign'); - $table->dropForeign('servers_option_foreign'); - $table->dropForeign('servers_pack_foreign'); - - $table->dropIndex('servers_node_foreign'); - $table->dropIndex('servers_owner_foreign'); - $table->dropIndex('servers_allocation_foreign'); - $table->dropIndex('servers_service_foreign'); - $table->dropIndex('servers_option_foreign'); - $table->dropIndex('servers_pack_foreign'); + $table->dropForeign(['node']); + $table->dropForeign(['owner']); + $table->dropForeign(['allocation']); + $table->dropForeign(['service']); + $table->dropForeign(['option']); + $table->dropForeign(['pack']); $table->renameColumn('node', 'node_id'); $table->renameColumn('owner', 'owner_id'); @@ -47,14 +40,10 @@ class UpdateColumnNames extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { - $table->dropForeign(['node_id']); - $table->dropForeign(['owner_id']); - $table->dropForeign(['allocation_id']); - $table->dropForeign(['service_id']); - $table->dropForeign(['option_id']); + $table->dropForeign(['node_id', 'owner_id', 'allocation_id', 'service_id', 'option_id']); $table->renameColumn('node_id', 'node'); $table->renameColumn('owner_id', 'owner'); diff --git a/database/migrations/2017_02_03_140948_UpdateNodesTable.php b/database/migrations/2017_02_03_140948_UpdateNodesTable.php index 58ec63ef4..e797cc704 100644 --- a/database/migrations/2017_02_03_140948_UpdateNodesTable.php +++ b/database/migrations/2017_02_03_140948_UpdateNodesTable.php @@ -9,11 +9,10 @@ class UpdateNodesTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('nodes', function (Blueprint $table) { - $table->dropForeign('nodes_location_foreign'); - $table->dropIndex('nodes_location_foreign'); + $table->dropForeign(['location']); $table->renameColumn('location', 'location_id'); $table->foreign('location_id')->references('id')->on('locations'); @@ -23,11 +22,10 @@ class UpdateNodesTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { - $table->dropForeign('nodes_location_id_foreign'); - $table->dropIndex('nodes_location_id_foreign'); + $table->dropForeign(['location_id']); $table->renameColumn('location_id', 'location'); $table->foreign('location')->references('id')->on('locations'); diff --git a/database/migrations/2017_02_03_155554_RenameColumns.php b/database/migrations/2017_02_03_155554_RenameColumns.php index 5f617abec..bd50e16be 100644 --- a/database/migrations/2017_02_03_155554_RenameColumns.php +++ b/database/migrations/2017_02_03_155554_RenameColumns.php @@ -9,13 +9,11 @@ class RenameColumns extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('allocations', function (Blueprint $table) { - $table->dropForeign('allocations_node_foreign'); - $table->dropForeign('allocations_assigned_to_foreign'); - $table->dropIndex('allocations_node_foreign'); - $table->dropIndex('allocations_assigned_to_foreign'); + $table->dropForeign(['node']); + $table->dropForeign(['assigned_to']); $table->renameColumn('node', 'node_id'); $table->renameColumn('assigned_to', 'server_id'); @@ -27,13 +25,13 @@ class RenameColumns extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('allocations', function (Blueprint $table) { - $table->dropForeign('allocations_node_id_foreign'); - $table->dropForeign('allocations_server_id_foreign'); - $table->dropIndex('allocations_node_id_foreign'); - $table->dropIndex('allocations_server_id_foreign'); + $table->dropForeign(['node_id']); + $table->dropForeign(['server_id']); + $table->dropIndex(['node_id']); + $table->dropIndex(['server_id']); $table->renameColumn('node_id', 'node'); $table->renameColumn('server_id', 'assigned_to'); diff --git a/database/migrations/2017_02_05_164123_AdjustColumnNames.php b/database/migrations/2017_02_05_164123_AdjustColumnNames.php index c7688f056..51c8818c7 100644 --- a/database/migrations/2017_02_05_164123_AdjustColumnNames.php +++ b/database/migrations/2017_02_05_164123_AdjustColumnNames.php @@ -9,11 +9,10 @@ class AdjustColumnNames extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_options', function (Blueprint $table) { - $table->dropForeign('service_options_parent_service_foreign'); - $table->dropIndex('service_options_parent_service_foreign'); + $table->dropForeign(['parent_service']); $table->renameColumn('parent_service', 'service_id'); $table->foreign('service_id')->references('id')->on('services'); @@ -23,11 +22,11 @@ class AdjustColumnNames extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { - $table->dropForeign('service_options_service_id_foreign'); - $table->dropIndex('service_options_service_id_foreign'); + $table->dropForeign(['service_id']); + $table->dropIndex(['service_id']); $table->renameColumn('service_id', 'parent_service'); $table->foreign('parent_service')->references('id')->on('services'); diff --git a/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php b/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php index 6f86b3b6e..69dc33dda 100644 --- a/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php +++ b/database/migrations/2017_02_05_164516_AdjustColumnNamesForServicePacks.php @@ -9,11 +9,10 @@ class AdjustColumnNamesForServicePacks extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_packs', function (Blueprint $table) { - $table->dropForeign('service_packs_option_foreign'); - $table->dropIndex('service_packs_option_foreign'); + $table->dropForeign(['option']); $table->renameColumn('option', 'option_id'); $table->foreign('option_id')->references('id')->on('service_options'); @@ -23,11 +22,11 @@ class AdjustColumnNamesForServicePacks extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_packs', function (Blueprint $table) { - $table->dropForeign('service_packs_option_id_foreign'); - $table->dropIndex('service_packs_option_id_foreign'); + $table->dropForeign(['option_id']); + $table->dropIndex(['option_id']); $table->renameColumn('option_id', 'option'); $table->foreign('option')->references('id')->on('service_options'); diff --git a/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php b/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php index 45efce83a..bf6469506 100644 --- a/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php +++ b/database/migrations/2017_02_09_174834_SetupPermissionsPivotTable.php @@ -11,7 +11,7 @@ class SetupPermissionsPivotTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('permissions', function (Blueprint $table) { $table->unsignedInteger('subuser_id')->after('id'); @@ -19,17 +19,15 @@ class SetupPermissionsPivotTable extends Migration DB::transaction(function () { foreach (Subuser::all() as &$subuser) { - Permission::where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->update([ + Permission::query()->where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->update([ 'subuser_id' => $subuser->id, ]); } }); Schema::table('permissions', function (Blueprint $table) { - $table->dropForeign('permissions_server_id_foreign'); - $table->dropIndex('permissions_server_id_foreign'); - $table->dropForeign('permissions_user_id_foreign'); - $table->dropIndex('permissions_user_id_foreign'); + $table->dropForeign(['server_id']); + $table->dropForeign(['user_id']); $table->dropColumn('server_id'); $table->dropColumn('user_id'); @@ -42,7 +40,7 @@ class SetupPermissionsPivotTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('permissions', function (Blueprint $table) { $table->unsignedInteger('server_id')->after('subuser_id'); @@ -52,7 +50,7 @@ class SetupPermissionsPivotTable extends Migration DB::transaction(function () { foreach (Subuser::all() as &$subuser) { - Permission::where('subuser_id', $subuser->id)->update([ + Permission::query()->where('subuser_id', $subuser->id)->update([ 'user_id' => $subuser->user_id, 'server_id' => $subuser->server_id, ]); @@ -60,8 +58,8 @@ class SetupPermissionsPivotTable extends Migration }); Schema::table('permissions', function (Blueprint $table) { - $table->dropForeign('permissions_subuser_id_foreign'); - $table->dropIndex('permissions_subuser_id_foreign'); + $table->dropForeign(['subuser_id']); + $table->dropIndex(['subuser_id']); $table->dropColumn('subuser_id'); $table->foreign('server_id')->references('id')->on('servers'); diff --git a/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php b/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php index 8b541d941..8ae28c2c9 100644 --- a/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php +++ b/database/migrations/2017_02_10_171858_UpdateAPIKeyColumnNames.php @@ -9,10 +9,10 @@ class UpdateAPIKeyColumnNames extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('api_keys', function (Blueprint $table) { - $table->dropForeign('api_keys_user_foreign')->dropIndex('api_keys_user_foreign'); + $table->dropForeign(['user']); $table->renameColumn('user', 'user_id'); $table->foreign('user_id')->references('id')->on('users'); @@ -22,10 +22,10 @@ class UpdateAPIKeyColumnNames extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('api_keys', function (Blueprint $table) { - $table->dropForeign('api_keys_user_id_foreign')->dropIndex('api_keys_user_id_foreign'); + $table->dropForeign(['user_id']); $table->renameColumn('user_id', 'user'); $table->foreign('user')->references('id')->on('users'); diff --git a/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php b/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php index 4f27346fa..aab6c2b95 100644 --- a/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php +++ b/database/migrations/2017_03_03_224254_UpdateNodeConfigTokensColumns.php @@ -9,7 +9,7 @@ class UpdateNodeConfigTokensColumns extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('node_configuration_tokens', function (Blueprint $table) { $table->dropForeign(['node']); @@ -23,7 +23,7 @@ class UpdateNodeConfigTokensColumns extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('node_configuration_tokens', function (Blueprint $table) { $table->dropForeign(['node_id']); diff --git a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php index 2918e1afd..d697a3315 100644 --- a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php +++ b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php @@ -9,22 +9,20 @@ class DeleteServiceExecutableOption extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - DB::transaction(function () { - Schema::table('services', function (Blueprint $table) { - $table->renameColumn('file', 'folder'); - $table->dropColumn('executable'); - $table->text('description')->nullable()->change(); - $table->text('startup')->nullable()->change(); - }); + Schema::table('services', function (Blueprint $table) { + $table->renameColumn('file', 'folder'); + $table->dropColumn('executable'); + $table->text('description')->nullable()->change(); + $table->text('startup')->nullable()->change(); }); } /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('services', function (Blueprint $table) { $table->string('executable')->after('folder'); diff --git a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php index 385004fa4..06c04694c 100644 --- a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php +++ b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php @@ -9,7 +9,7 @@ class AddNewServiceOptionsColumns extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropColumn('executable'); @@ -27,7 +27,7 @@ class AddNewServiceOptionsColumns extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropForeign(['config_from']); diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php index 039976352..40aef1524 100644 --- a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -1,11 +1,5 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ + use Illuminate\Database\Migrations\Migration; class MigrateToNewServiceSystem extends Migration @@ -13,16 +7,16 @@ class MigrateToNewServiceSystem extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::transaction(function () { $service = DB::table('services')->where('author', config('pterodactyl.service.core'))->where('folder', 'srcds')->first(); - if (! $service) { + if (!$service) { return; } $options = DB::table('service_options')->where('service_id', $service->id)->get(); - $options->each(function ($item) use ($options) { + $options->each(function ($item) { if ($item->tag === 'srcds' && $item->name === 'Insurgency') { $item->tag = 'insurgency'; } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { @@ -38,7 +32,7 @@ class MigrateToNewServiceSystem extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { // Not doing reversals right now... } diff --git a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php index 21fa51465..3e7e5f18b 100644 --- a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php +++ b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php @@ -9,7 +9,7 @@ class ChangeServiceVariablesValidationRules extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_variables', function (Blueprint $table) { $table->renameColumn('regex', 'rules'); @@ -30,7 +30,7 @@ class ChangeServiceVariablesValidationRules extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_variables', function (Blueprint $table) { $table->renameColumn('rules', 'regex'); diff --git a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php index 3628ba7a4..26599246c 100644 --- a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php +++ b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php @@ -85,7 +85,7 @@ EOF; /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('services', function (Blueprint $table) { $table->text('index_file')->after('startup'); @@ -105,7 +105,7 @@ EOF; /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('services', function (Blueprint $table) { $table->dropColumn('index_file'); diff --git a/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php b/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php index d01012e41..f73befdba 100644 --- a/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php +++ b/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php @@ -9,7 +9,7 @@ class RenameServicePacksToSingluarPacks extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_packs', function (Blueprint $table) { $table->dropForeign(['option_id']); @@ -25,7 +25,7 @@ class RenameServicePacksToSingluarPacks extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('packs', function (Blueprint $table) { $table->dropForeign(['option_id']); diff --git a/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php b/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php index b1a8ee3a0..b396954e0 100644 --- a/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php +++ b/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php @@ -9,7 +9,7 @@ class AddLockedStatusToTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('packs', function (Blueprint $table) { $table->boolean('locked')->default(false)->after('visible'); @@ -19,7 +19,7 @@ class AddLockedStatusToTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('packs', function (Blueprint $table) { $table->dropColumn('locked'); diff --git a/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php b/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php index a7166df9e..c973faa55 100644 --- a/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php +++ b/database/migrations/2017_03_16_181109_ReOrganizeDatabaseServersToDatabaseHost.php @@ -9,7 +9,7 @@ class ReOrganizeDatabaseServersToDatabaseHost extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('database_servers', function (Blueprint $table) { $table->dropForeign(['linked_node']); @@ -27,7 +27,7 @@ class ReOrganizeDatabaseServersToDatabaseHost extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('database_hosts', function (Blueprint $table) { $table->dropForeign(['node_id']); diff --git a/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php b/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php index bc6fb45c7..2b689c481 100644 --- a/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php +++ b/database/migrations/2017_03_16_181515_CleanupDatabasesDatabase.php @@ -9,7 +9,7 @@ class CleanupDatabasesDatabase extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('databases', function (Blueprint $table) { $table->dropForeign(['db_server']); @@ -23,7 +23,7 @@ class CleanupDatabasesDatabase extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('databases', function (Blueprint $table) { $table->dropForeign(['database_host_id']); diff --git a/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php b/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php index 3f26a1e34..bdd1f1a54 100644 --- a/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php +++ b/database/migrations/2017_03_18_204953_AddForeignKeyToPacks.php @@ -9,7 +9,7 @@ class AddForeignKeyToPacks extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->foreign('pack_id')->references('id')->on('packs'); @@ -19,7 +19,7 @@ class AddForeignKeyToPacks extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropForeign(['pack_id']); diff --git a/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php b/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php index e8ebcb20d..69d044582 100644 --- a/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php +++ b/database/migrations/2017_03_31_221948_AddServerDescriptionColumn.php @@ -9,7 +9,7 @@ class AddServerDescriptionColumn extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->text('description')->after('name'); @@ -19,7 +19,7 @@ class AddServerDescriptionColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('description'); diff --git a/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php index 3cd08f1a9..0c193192b 100644 --- a/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php +++ b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php @@ -9,7 +9,7 @@ class DropDeletedAtColumnFromServers extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('deleted_at'); @@ -19,7 +19,7 @@ class DropDeletedAtColumnFromServers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->timestamp('deleted_at')->nullable(); diff --git a/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php b/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php index d069e1ba1..f5e48cfea 100644 --- a/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php +++ b/database/migrations/2017_04_15_125021_UpgradeTaskSystem.php @@ -10,7 +10,7 @@ class UpgradeTaskSystem extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('tasks', function (Blueprint $table) { $table->dropForeign(['server']); @@ -33,7 +33,7 @@ class UpgradeTaskSystem extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('tasks', function (Blueprint $table) { // $table->dropForeign(['server_id']); diff --git a/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php b/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php index ba2f57c41..96a85be92 100644 --- a/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php +++ b/database/migrations/2017_04_20_171943_AddScriptsToServiceOptions.php @@ -9,7 +9,7 @@ class AddScriptsToServiceOptions extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_options', function (Blueprint $table) { $table->text('script_install')->after('startup')->nullable(); @@ -22,7 +22,7 @@ class AddScriptsToServiceOptions extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropColumn('script_install'); diff --git a/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php b/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php index 2bc8f27b3..970b41773 100644 --- a/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php +++ b/database/migrations/2017_04_21_151432_AddServiceScriptTrackingToServers.php @@ -9,7 +9,7 @@ class AddServiceScriptTrackingToServers extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->boolean('skip_scripts')->default(false)->after('description'); @@ -19,7 +19,7 @@ class AddServiceScriptTrackingToServers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('skip_scripts'); diff --git a/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php b/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php index 514d17e1c..8888600fb 100644 --- a/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php +++ b/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php @@ -9,7 +9,7 @@ class AddCopyScriptFromColumn extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_options', function (Blueprint $table) { $table->unsignedInteger('copy_script_from')->nullable()->after('script_container'); @@ -21,7 +21,7 @@ class AddCopyScriptFromColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropForeign(['copy_script_from']); diff --git a/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php b/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php index aa5e04498..96bb9aec5 100644 --- a/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php +++ b/database/migrations/2017_04_27_223629_AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy.php @@ -9,7 +9,7 @@ class AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('nodes', function (Blueprint $table) { $table->boolean('behind_proxy')->after('scheme')->default(false); @@ -19,7 +19,7 @@ class AddAbilityToDefineConnectionOverSSLWithDaemonBehindProxy extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { $table->dropColumn('behind_proxy'); diff --git a/database/migrations/2017_05_01_141528_DeleteDownloadTable.php b/database/migrations/2017_05_01_141528_DeleteDownloadTable.php index 7dcae3c6f..967c12615 100644 --- a/database/migrations/2017_05_01_141528_DeleteDownloadTable.php +++ b/database/migrations/2017_05_01_141528_DeleteDownloadTable.php @@ -9,7 +9,7 @@ class DeleteDownloadTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::dropIfExists('downloads'); } @@ -17,7 +17,7 @@ class DeleteDownloadTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::create('downloads', function (Blueprint $table) { $table->increments('id'); diff --git a/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php b/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php index 90c8c4b1e..d230bc19a 100644 --- a/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php +++ b/database/migrations/2017_05_01_141559_DeleteNodeConfigurationTable.php @@ -9,7 +9,7 @@ class DeleteNodeConfigurationTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::dropIfExists('node_configuration_tokens'); } @@ -17,7 +17,7 @@ class DeleteNodeConfigurationTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::create('node_configuration_tokens', function (Blueprint $table) { $table->increments('id'); diff --git a/database/migrations/2017_06_10_152951_add_external_id_to_users.php b/database/migrations/2017_06_10_152951_add_external_id_to_users.php index 9ce5057e8..bccfb43fd 100644 --- a/database/migrations/2017_06_10_152951_add_external_id_to_users.php +++ b/database/migrations/2017_06_10_152951_add_external_id_to_users.php @@ -9,7 +9,7 @@ class AddExternalIdToUsers extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->unsignedInteger('external_id')->after('id')->nullable()->unique(); @@ -19,7 +19,7 @@ class AddExternalIdToUsers extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('external_id'); diff --git a/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php b/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php index a089ab4db..6f36d0e05 100644 --- a/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php +++ b/database/migrations/2017_06_25_133923_ChangeForeignKeyToBeOnCascadeDelete.php @@ -9,7 +9,7 @@ class ChangeForeignKeyToBeOnCascadeDelete extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('api_permissions', function (Blueprint $table) { $table->dropForeign(['key_id']); @@ -21,7 +21,7 @@ class ChangeForeignKeyToBeOnCascadeDelete extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('api_permissions', function (Blueprint $table) { $table->dropForeign(['key_id']); diff --git a/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php b/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php index 0bfc7d527..10058c8cc 100644 --- a/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php +++ b/database/migrations/2017_07_08_152806_ChangeUserPermissionsToDeleteOnUserDeletion.php @@ -9,7 +9,7 @@ class ChangeUserPermissionsToDeleteOnUserDeletion extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('permissions', function (Blueprint $table) { $table->dropForeign(['subuser_id']); @@ -29,7 +29,7 @@ class ChangeUserPermissionsToDeleteOnUserDeletion extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('subusers', function (Blueprint $table) { $table->dropForeign(['user_id']); diff --git a/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php b/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php index fb156ba8c..8ac6eccec 100644 --- a/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php +++ b/database/migrations/2017_07_08_154416_SetAllocationToReferenceNullOnServerDelete.php @@ -9,7 +9,7 @@ class SetAllocationToReferenceNullOnServerDelete extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('allocations', function (Blueprint $table) { $table->dropForeign(['server_id']); @@ -21,7 +21,7 @@ class SetAllocationToReferenceNullOnServerDelete extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('allocations', function (Blueprint $table) { $table->dropForeign(['server_id']); diff --git a/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php b/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php index 5ae9a29f9..ca5a4623f 100644 --- a/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php +++ b/database/migrations/2017_07_08_154650_CascadeDeletionWhenAServerOrVariableIsDeleted.php @@ -9,7 +9,7 @@ class CascadeDeletionWhenAServerOrVariableIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('server_variables', function (Blueprint $table) { $table->dropForeign(['server_id']); @@ -23,7 +23,7 @@ class CascadeDeletionWhenAServerOrVariableIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('server_variables', function (Blueprint $table) { $table->dropForeign(['server_id']); diff --git a/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php b/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php index 042e7564c..cf0a4bba1 100644 --- a/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php +++ b/database/migrations/2017_07_24_194433_DeleteTaskWhenParentServerIsDeleted.php @@ -9,7 +9,7 @@ class DeleteTaskWhenParentServerIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('tasks', function (Blueprint $table) { $table->dropForeign(['server_id']); @@ -21,8 +21,7 @@ class DeleteTaskWhenParentServerIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { - // } } diff --git a/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php b/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php index a33b78af6..0eabe77db 100644 --- a/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php +++ b/database/migrations/2017_08_05_115800_CascadeNullValuesForDatabaseHostWhenNodeIsDeleted.php @@ -9,7 +9,7 @@ class CascadeNullValuesForDatabaseHostWhenNodeIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('database_hosts', function (Blueprint $table) { $table->dropForeign(['node_id']); @@ -20,7 +20,7 @@ class CascadeNullValuesForDatabaseHostWhenNodeIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('database_hosts', function (Blueprint $table) { $table->dropForeign(['node_id']); diff --git a/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php b/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php index 77b7f984c..3fb457dc4 100644 --- a/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php +++ b/database/migrations/2017_08_05_144104_AllowNegativeValuesForOverallocation.php @@ -9,7 +9,7 @@ class AllowNegativeValuesForOverallocation extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('nodes', function (Blueprint $table) { $table->integer('disk_overallocate')->default(0)->nullable(false)->change(); @@ -20,10 +20,10 @@ class AllowNegativeValuesForOverallocation extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { - DB::statement('ALTER TABLE nodes MODIFY disk_overallocate MEDIUMINT UNSIGNED NULL, + DB::statement('ALTER TABLE nodes MODIFY disk_overallocate MEDIUMINT UNSIGNED NULL, MODIFY memory_overallocate MEDIUMINT UNSIGNED NULL'); }); } diff --git a/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php b/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php index f7aab7c04..f2d8ad9bf 100644 --- a/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php +++ b/database/migrations/2017_08_05_174811_SetAllocationUnqiueUsingMultipleFields.php @@ -9,7 +9,7 @@ class SetAllocationUnqiueUsingMultipleFields extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('allocations', function (Blueprint $table) { $table->unique(['node_id', 'ip', 'port']); @@ -19,7 +19,7 @@ class SetAllocationUnqiueUsingMultipleFields extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('allocations', function (Blueprint $table) { $table->dropForeign(['node_id']); diff --git a/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php b/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php index 074f872e0..fbea750bc 100644 --- a/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php +++ b/database/migrations/2017_08_15_214555_CascadeDeletionWhenAParentServiceIsDeleted.php @@ -9,7 +9,7 @@ class CascadeDeletionWhenAParentServiceIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropForeign(['service_id']); @@ -21,7 +21,7 @@ class CascadeDeletionWhenAParentServiceIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropForeign(['service_id']); diff --git a/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php b/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php index 1b8f1a567..7c59d801e 100644 --- a/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php +++ b/database/migrations/2017_08_18_215428_RemovePackWhenParentServiceOptionIsDeleted.php @@ -9,7 +9,7 @@ class RemovePackWhenParentServiceOptionIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('packs', function (Blueprint $table) { $table->dropForeign(['option_id']); @@ -21,7 +21,7 @@ class RemovePackWhenParentServiceOptionIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('packs', function (Blueprint $table) { $table->dropForeign(['option_id']); diff --git a/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php b/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php index 12eada73c..14f60b3b6 100644 --- a/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php +++ b/database/migrations/2017_09_10_225749_RenameTasksTableForStructureRefactor.php @@ -8,7 +8,7 @@ class RenameTasksTableForStructureRefactor extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::rename('tasks', 'tasks_old'); } @@ -16,7 +16,7 @@ class RenameTasksTableForStructureRefactor extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::rename('tasks_old', 'tasks'); } diff --git a/database/migrations/2017_09_10_225941_CreateSchedulesTable.php b/database/migrations/2017_09_10_225941_CreateSchedulesTable.php index 3d5baa6d3..588f48c8f 100644 --- a/database/migrations/2017_09_10_225941_CreateSchedulesTable.php +++ b/database/migrations/2017_09_10_225941_CreateSchedulesTable.php @@ -9,7 +9,7 @@ class CreateSchedulesTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::create('schedules', function (Blueprint $table) { $table->increments('id'); @@ -32,7 +32,7 @@ class CreateSchedulesTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('schedules'); } diff --git a/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php b/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php index 9c225a834..969c15361 100644 --- a/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php +++ b/database/migrations/2017_09_10_230309_CreateNewTasksTableForSchedules.php @@ -9,7 +9,7 @@ class CreateNewTasksTableForSchedules extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->increments('id'); @@ -29,7 +29,7 @@ class CreateNewTasksTableForSchedules extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('tasks'); } diff --git a/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php b/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php index 2a20ef10e..4656e272e 100644 --- a/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php +++ b/database/migrations/2017_09_11_002938_TransferOldTasksToNewScheduler.php @@ -2,6 +2,7 @@ use Carbon\Carbon; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; @@ -10,40 +11,40 @@ class TransferOldTasksToNewScheduler extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { - $tasks = DB::table('tasks_old')->get(); + DB::transaction(function () { + $tasks = DB::table('tasks_old')->get(); - DB::beginTransaction(); - $tasks->each(function ($task) { - $schedule = DB::table('schedules')->insertGetId([ - 'server_id' => $task->server_id, - 'name' => null, - 'cron_day_of_week' => $task->day_of_week, - 'cron_day_of_month' => $task->day_of_month, - 'cron_hour' => $task->hour, - 'cron_minute' => $task->minute, - 'is_active' => (bool) $task->active, - 'is_processing' => false, - 'last_run_at' => $task->last_run, - 'next_run_at' => $task->next_run, - 'created_at' => $task->created_at, - 'updated_at' => Carbon::now()->toDateTimeString(), - ]); + $tasks->each(function ($task) { + $schedule = DB::table('schedules')->insertGetId([ + 'server_id' => $task->server_id, + 'name' => null, + 'cron_day_of_week' => $task->day_of_week, + 'cron_day_of_month' => $task->day_of_month, + 'cron_hour' => $task->hour, + 'cron_minute' => $task->minute, + 'is_active' => (bool) $task->active, + 'is_processing' => false, + 'last_run_at' => $task->last_run, + 'next_run_at' => $task->next_run, + 'created_at' => $task->created_at, + 'updated_at' => Carbon::now()->toDateTimeString(), + ]); - DB::table('tasks')->insert([ - 'schedule_id' => $schedule, - 'sequence_id' => 1, - 'action' => $task->action, - 'payload' => $task->data, - 'time_offset' => 0, - 'is_queued' => false, - 'updated_at' => Carbon::now()->toDateTimeString(), - 'created_at' => Carbon::now()->toDateTimeString(), - ]); + DB::table('tasks')->insert([ + 'schedule_id' => $schedule, + 'sequence_id' => 1, + 'action' => $task->action, + 'payload' => $task->data, + 'time_offset' => 0, + 'is_queued' => false, + 'updated_at' => Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + ]); - DB::table('tasks_old')->delete($task->id); - DB::commit(); + DB::table('tasks_old')->delete($task->id); + }); }); Schema::dropIfExists('tasks_old'); @@ -52,7 +53,7 @@ class TransferOldTasksToNewScheduler extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::create('tasks_old', function (Blueprint $table) { $table->increments('id'); diff --git a/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php b/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php index b7c361f13..7c0c57447 100644 --- a/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php +++ b/database/migrations/2017_09_13_211810_UpdateOldPermissionsToPointToNewScheduleSystem.php @@ -8,12 +8,12 @@ class UpdateOldPermissionsToPointToNewScheduleSystem extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { $permissions = DB::table('permissions')->where('permission', 'like', '%-task%')->get(); foreach ($permissions as $record) { $parts = explode('-', $record->permission); - if (! in_array(array_get($parts, 1), ['tasks', 'task']) || count($parts) !== 2) { + if (!in_array(array_get($parts, 1), ['tasks', 'task']) || count($parts) !== 2) { continue; } @@ -26,12 +26,12 @@ class UpdateOldPermissionsToPointToNewScheduleSystem extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { $permissions = DB::table('permissions')->where('permission', 'like', '%-schedule%')->get(); foreach ($permissions as $record) { $parts = explode('-', $record->permission); - if (! in_array(array_get($parts, 1), ['schedules', 'schedule']) || count($parts) !== 2) { + if (!in_array(array_get($parts, 1), ['schedules', 'schedule']) || count($parts) !== 2) { continue; } diff --git a/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php b/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php index cfbfc88b0..64ff02666 100644 --- a/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php +++ b/database/migrations/2017_09_23_170933_CreateDaemonKeysTable.php @@ -9,7 +9,7 @@ class CreateDaemonKeysTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::create('daemon_keys', function (Blueprint $table) { $table->increments('id'); @@ -28,7 +28,7 @@ class CreateDaemonKeysTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::dropIfExists('daemon_keys'); } diff --git a/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php b/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php index 84cb2d92b..b284905a0 100644 --- a/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php +++ b/database/migrations/2017_09_23_173628_RemoveDaemonSecretFromServersTable.php @@ -12,7 +12,7 @@ class RemoveDaemonSecretFromServersTable extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { $inserts = []; @@ -41,7 +41,7 @@ class RemoveDaemonSecretFromServersTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->char('daemonSecret', 36)->after('startup')->unique(); diff --git a/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php b/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php index d4d2dd695..9ea90cff2 100644 --- a/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php +++ b/database/migrations/2017_09_23_185022_RemoveDaemonSecretFromSubusersTable.php @@ -1,6 +1,7 @@ get(); @@ -39,7 +40,7 @@ class RemoveDaemonSecretFromSubusersTable extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('subusers', function (Blueprint $table) { $table->char('daemonSecret', 36)->after('server_id'); diff --git a/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php b/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php index 6bb36813d..aae62921a 100644 --- a/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php +++ b/database/migrations/2017_10_02_202000_ChangeServicesToUseAMoreUniqueIdentifier.php @@ -11,7 +11,7 @@ class ChangeServicesToUseAMoreUniqueIdentifier extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('services', function (Blueprint $table) { $table->dropUnique(['name']); @@ -26,8 +26,8 @@ class ChangeServicesToUseAMoreUniqueIdentifier extends Migration DB::table('services')->get(['id', 'author', 'uuid'])->each(function ($service) { DB::table('services')->where('id', $service->id)->update([ - 'author' => ($service->author === 'ptrdctyl-v040-11e6-8b77-86f30ca893d3') ? 'support@pterodactyl.io' : 'unknown@unknown-author.com', - 'uuid' => Uuid::uuid4()->toString(), + 'author' => ($service->author === 'ptrdctyl-v040-11e6-8b77-86f30ca893d3') ? 'support@pterodactyl.io' : 'unknown@unknown-author.com', + 'uuid' => Uuid::uuid4()->toString(), ]); }); @@ -39,7 +39,7 @@ class ChangeServicesToUseAMoreUniqueIdentifier extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('services', function (Blueprint $table) { $table->dropColumn('uuid'); diff --git a/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php b/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php index 5c9df79a5..679a8b5e0 100644 --- a/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php +++ b/database/migrations/2017_10_02_202007_ChangeToABetterUniqueServiceConfiguration.php @@ -11,7 +11,7 @@ class ChangeToABetterUniqueServiceConfiguration extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_options', function (Blueprint $table) { $table->char('uuid', 36)->after('id'); @@ -40,7 +40,7 @@ class ChangeToABetterUniqueServiceConfiguration extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_options', function (Blueprint $table) { $table->dropColumn('uuid'); diff --git a/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php b/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php index 3b19e3d99..9a64abf07 100644 --- a/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php +++ b/database/migrations/2017_10_03_233202_CascadeDeletionWhenServiceOptionIsDeleted.php @@ -9,7 +9,7 @@ class CascadeDeletionWhenServiceOptionIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('service_variables', function (Blueprint $table) { $table->dropForeign(['option_id']); @@ -21,7 +21,7 @@ class CascadeDeletionWhenServiceOptionIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('service_variables', function (Blueprint $table) { $table->dropForeign(['option_id']); diff --git a/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php b/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php index e7b70136c..cea7460e6 100644 --- a/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php +++ b/database/migrations/2017_10_06_214026_ServicesToNestsConversion.php @@ -1,5 +1,6 @@ dropUnique(['username']); @@ -22,7 +22,7 @@ class RemoveLegacySFTPInformation extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->string('username')->nullable()->after('image')->unique(); diff --git a/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php b/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php index 53cb6526b..b02e326c6 100644 --- a/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php +++ b/database/migrations/2017_11_11_161922_Add2FaLastAuthorizationTimeColumn.php @@ -12,7 +12,7 @@ class Add2FaLastAuthorizationTimeColumn extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->text('totp_secret')->nullable()->change(); @@ -27,7 +27,7 @@ class Add2FaLastAuthorizationTimeColumn extends Migration DB::table('users')->where('id', $user->id)->update([ 'totp_secret' => Crypt::encrypt($user->totp_secret), - 'updated_at' => Carbon::now()->toIso8601String(), + 'updated_at' => Carbon::now()->toAtomString(), ]); }); }); @@ -36,7 +36,7 @@ class Add2FaLastAuthorizationTimeColumn extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { DB::transaction(function () { DB::table('users')->get()->each(function ($user) { @@ -46,7 +46,7 @@ class Add2FaLastAuthorizationTimeColumn extends Migration DB::table('users')->where('id', $user->id)->update([ 'totp_secret' => Crypt::decrypt($user->totp_secret), - 'updated_at' => Carbon::now()->toIso8601String(), + 'updated_at' => Carbon::now()->toAtomString(), ]); }); }); diff --git a/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php b/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php index c2947ee07..229827dbc 100644 --- a/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php +++ b/database/migrations/2017_11_19_122708_MigratePubPrivFormatToSingleKey.php @@ -1,5 +1,6 @@ get()->each(function ($item) { try { $decrypted = Crypt::decrypt($item->secret); - } catch (DecryptException $exception) { + } catch (DecryptException) { $decrypted = str_random(32); } finally { DB::table('api_keys')->where('id', $item->id)->update([ @@ -30,27 +31,41 @@ class MigratePubPrivFormatToSingleKey extends Migration Schema::table('api_keys', function (Blueprint $table) { $table->dropColumn('public'); - $table->string('secret', 32)->change(); + $table->renameColumn('secret', 'token'); }); - DB::statement('ALTER TABLE `api_keys` CHANGE `secret` `token` CHAR(32) NOT NULL, ADD UNIQUE INDEX `api_keys_token_unique` (`token`(32))'); + Schema::table('api_keys', function (Blueprint $table) { + $table->char('token', 32)->change(); + $table->unique('token'); + }); } /** * Reverse the migrations. */ - public function down() + public function down(): void { - DB::statement('ALTER TABLE `api_keys` CHANGE `token` `secret` TEXT, DROP INDEX `api_keys_token_unique`'); + Schema::table('api_keys', function (Blueprint $table) { + $table->dropUnique(['token']); + $table->renameColumn('token', 'secret'); + }); Schema::table('api_keys', function (Blueprint $table) { + $table->dropUnique('token'); + $table->text('token')->change(); + }); + + Schema::table('api_keys', function (Blueprint $table) { + $table->renameColumn('token', 'secret'); + + $table->text('secret')->nullable()->change(); $table->char('public', 16)->after('user_id'); }); DB::transaction(function () { DB::table('api_keys')->get()->each(function ($item) { DB::table('api_keys')->where('id', $item->id)->update([ - 'public' => str_random(16), + 'public' => Str::random(16), 'secret' => Crypt::encrypt($item->secret), ]); }); diff --git a/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php b/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php index d28109598..9a0cc9114 100644 --- a/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php +++ b/database/migrations/2017_12_04_184012_DropAllocationsWhenNodeIsDeleted.php @@ -9,7 +9,7 @@ class DropAllocationsWhenNodeIsDeleted extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { Schema::table('allocations', function (Blueprint $table) { $table->dropForeign(['node_id']); @@ -21,7 +21,7 @@ class DropAllocationsWhenNodeIsDeleted extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('allocations', function (Blueprint $table) { $table->dropForeign(['node_id']); diff --git a/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php b/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php index 1bdaf6477..8d639a37a 100644 --- a/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php +++ b/database/migrations/2017_12_12_220426_MigrateSettingsTableToNewFormat.php @@ -10,7 +10,7 @@ class MigrateSettingsTableToNewFormat extends Migration /** * Run the migrations. */ - public function up() + public function up(): void { DB::table('settings')->truncate(); Schema::table('settings', function (Blueprint $table) { @@ -21,7 +21,7 @@ class MigrateSettingsTableToNewFormat extends Migration /** * Reverse the migrations. */ - public function down() + public function down(): void { Schema::table('settings', function (Blueprint $table) { $table->dropColumn('id'); diff --git a/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php b/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php index 8f9938da1..7ccae5d61 100644 --- a/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php +++ b/database/migrations/2018_01_01_122821_AllowNegativeValuesForServerSwap.php @@ -8,10 +8,8 @@ class AllowNegativeValuesForServerSwap extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->integer('swap')->change(); @@ -20,10 +18,8 @@ class AllowNegativeValuesForServerSwap extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->unsignedInteger('swap')->change(); diff --git a/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php b/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php index adc6d2648..118a422f4 100644 --- a/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php +++ b/database/migrations/2018_01_11_213943_AddApiKeyPermissionColumns.php @@ -8,10 +8,8 @@ class AddApiKeyPermissionColumns extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::dropIfExists('api_permissions'); @@ -31,10 +29,8 @@ class AddApiKeyPermissionColumns extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::create('api_permissions', function (Blueprint $table) { $table->increments('id'); diff --git a/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php b/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php index 5dba9c113..d7e33210d 100644 --- a/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php +++ b/database/migrations/2018_01_13_142012_SetupTableForKeyEncryption.php @@ -9,11 +9,10 @@ class SetupTableForKeyEncryption extends Migration /** * Run the migrations. * - * @return void * @throws \Exception * @throws \Throwable */ - public function up() + public function up(): void { Schema::table('api_keys', function (Blueprint $table) { $table->char('identifier', 16)->nullable()->unique()->after('user_id'); @@ -28,11 +27,10 @@ class SetupTableForKeyEncryption extends Migration /** * Reverse the migrations. * - * @return void * @throws \Exception * @throws \Throwable */ - public function down() + public function down(): void { Schema::table('api_keys', function (Blueprint $table) { $table->dropColumn('identifier'); diff --git a/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php b/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php index e0f86b9de..f78f7a5d1 100644 --- a/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php +++ b/database/migrations/2018_01_13_145209_AddLastUsedAtColumn.php @@ -8,10 +8,8 @@ class AddLastUsedAtColumn extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('api_keys', function (Blueprint $table) { $table->unsignedTinyInteger('key_type')->after('user_id')->default(0); @@ -28,10 +26,8 @@ class AddLastUsedAtColumn extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('api_keys', function (Blueprint $table) { $table->timestamp('expires_at')->after('memo')->nullable(); diff --git a/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php b/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php index 6a4a04e7d..6166f016e 100644 --- a/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php +++ b/database/migrations/2018_02_04_145617_AllowTextInUserExternalId.php @@ -8,10 +8,8 @@ class AllowTextInUserExternalId extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('external_id')->nullable()->change(); @@ -20,10 +18,8 @@ class AllowTextInUserExternalId extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->unsignedInteger('external_id')->change(); diff --git a/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php b/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php index b587cdcb0..64dbaf0dc 100644 --- a/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php +++ b/database/migrations/2018_02_10_151150_remove_unique_index_on_external_id_column.php @@ -8,10 +8,8 @@ class RemoveUniqueIndexOnExternalIdColumn extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->dropUnique(['external_id']); @@ -20,10 +18,8 @@ class RemoveUniqueIndexOnExternalIdColumn extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->unique(['external_id']); diff --git a/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php b/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php index bff7bbfb0..99f1db454 100644 --- a/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php +++ b/database/migrations/2018_02_17_134254_ensure_unique_allocation_id_on_servers_table.php @@ -8,10 +8,8 @@ class EnsureUniqueAllocationIdOnServersTable extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->unique(['allocation_id']); @@ -20,10 +18,8 @@ class EnsureUniqueAllocationIdOnServersTable extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropForeign(['allocation_id']); diff --git a/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php b/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php index 2c8af99e2..c7d0f8fd4 100644 --- a/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php +++ b/database/migrations/2018_02_24_112356_add_external_id_column_to_servers_table.php @@ -8,10 +8,8 @@ class AddExternalIdColumnToServersTable extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->string('external_id')->after('id')->nullable()->unique(); @@ -20,10 +18,8 @@ class AddExternalIdColumnToServersTable extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn('external_id'); diff --git a/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php b/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php index 6469867f2..e432e56dd 100644 --- a/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php +++ b/database/migrations/2018_02_25_160152_remove_default_null_value_on_table.php @@ -13,7 +13,7 @@ class RemoveDefaultNullValueOnTable extends Migration * @throws \Exception * @throws \Throwable */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('external_id')->default(null)->change(); @@ -28,10 +28,8 @@ class RemoveDefaultNullValueOnTable extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // This should not be rolled back. } diff --git a/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php b/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php index 0a9b8afe2..38469af23 100644 --- a/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php +++ b/database/migrations/2018_02_25_160604_define_unique_index_on_users_external_id.php @@ -8,10 +8,8 @@ class DefineUniqueIndexOnUsersExternalId extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->index(['external_id']); @@ -20,10 +18,8 @@ class DefineUniqueIndexOnUsersExternalId extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropIndex(['external_id']); diff --git a/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php b/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php index 4e85e8aeb..00fbd11c2 100644 --- a/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php +++ b/database/migrations/2018_03_01_192831_add_database_and_port_limit_columns_to_servers_table.php @@ -8,10 +8,8 @@ class AddDatabaseAndPortLimitColumnsToServersTable extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->unsignedInteger('database_limit')->after('installed')->nullable()->default(0); @@ -21,10 +19,8 @@ class AddDatabaseAndPortLimitColumnsToServersTable extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->dropColumn(['database_limit', 'allocation_limit']); diff --git a/database/migrations/2018_03_15_124536_add_description_to_nodes.php b/database/migrations/2018_03_15_124536_add_description_to_nodes.php index 7208a4207..a5c1b7542 100644 --- a/database/migrations/2018_03_15_124536_add_description_to_nodes.php +++ b/database/migrations/2018_03_15_124536_add_description_to_nodes.php @@ -8,10 +8,8 @@ class AddDescriptionToNodes extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('nodes', function (Blueprint $table) { $table->text('description')->after('name'); @@ -20,10 +18,8 @@ class AddDescriptionToNodes extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { $table->dropColumn('description'); diff --git a/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php b/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php index 04fdf000f..e85eca8cd 100644 --- a/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php +++ b/database/migrations/2018_05_04_123826_add_maintenance_to_nodes.php @@ -8,10 +8,8 @@ class AddMaintenanceToNodes extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('nodes', function (Blueprint $table) { $table->boolean('maintenance_mode')->after('behind_proxy')->default(false); @@ -20,10 +18,8 @@ class AddMaintenanceToNodes extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('nodes', function (Blueprint $table) { $table->dropColumn('maintenance_mode'); diff --git a/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php b/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php index 199650940..e7a4089fa 100644 --- a/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php +++ b/database/migrations/2018_09_03_143756_allow_egg_variables_to_have_longer_values.php @@ -8,10 +8,8 @@ class AllowEggVariablesToHaveLongerValues extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('egg_variables', function (Blueprint $table) { $table->text('default_value')->change(); @@ -20,10 +18,8 @@ class AllowEggVariablesToHaveLongerValues extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('egg_variables', function (Blueprint $table) { $table->string('default_value')->change(); diff --git a/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php b/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php index cc90e0e06..a1d581819 100644 --- a/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php +++ b/database/migrations/2018_09_03_144005_allow_server_variables_to_have_longer_values.php @@ -8,10 +8,8 @@ class AllowServerVariablesToHaveLongerValues extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('server_variables', function (Blueprint $table) { $table->text('variable_value')->change(); @@ -20,10 +18,8 @@ class AllowServerVariablesToHaveLongerValues extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('server_variables', function (Blueprint $table) { $table->string('variable_value')->change(); diff --git a/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php b/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php index d91ce6372..6d43197de 100644 --- a/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php +++ b/database/migrations/2019_03_02_142328_set_allocation_limit_default_null.php @@ -8,10 +8,8 @@ class SetAllocationLimitDefaultNull extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('servers', function (Blueprint $table) { $table->unsignedInteger('allocation_limit')->nullable()->default(null)->change(); @@ -20,10 +18,8 @@ class SetAllocationLimitDefaultNull extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('servers', function (Blueprint $table) { $table->unsignedInteger('allocation_limit')->nullable()->default(0)->change(); diff --git a/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php b/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php index fe5f85f88..de110a06b 100644 --- a/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php +++ b/database/migrations/2019_03_02_151321_fix_unique_index_to_account_for_host.php @@ -8,10 +8,8 @@ class FixUniqueIndexToAccountForHost extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('databases', function (Blueprint $table) { $table->dropUnique(['database']); @@ -24,12 +22,12 @@ class FixUniqueIndexToAccountForHost extends Migration /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('databases', function (Blueprint $table) { + $table->dropForeign(['database_host_id']); + $table->dropUnique(['database_host_id', 'database']); $table->dropUnique(['database_host_id', 'username']); diff --git a/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php new file mode 100644 index 000000000..b71189fe0 --- /dev/null +++ b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php @@ -0,0 +1,130 @@ + P::ACTION_CONTROL_START, + 'power-stop' => P::ACTION_CONTROL_STOP, + 'power-restart' => P::ACTION_CONTROL_RESTART, + 'power-kill' => P::ACTION_CONTROL_STOP, + 'send-command' => P::ACTION_CONTROL_CONSOLE, + 'list-subusers' => P::ACTION_USER_READ, + 'view-subuser' => P::ACTION_USER_READ, + 'edit-subuser' => P::ACTION_USER_UPDATE, + 'create-subuser' => P::ACTION_USER_CREATE, + 'delete-subuser' => P::ACTION_USER_DELETE, + 'view-allocations' => P::ACTION_ALLOCATION_READ, + 'edit-allocation' => P::ACTION_ALLOCATION_UPDATE, + 'view-startup' => P::ACTION_STARTUP_READ, + 'edit-startup' => P::ACTION_STARTUP_UPDATE, + 'view-databases' => P::ACTION_DATABASE_READ, + // Better to just break this flow a bit than accidentally grant a dangerous permission. + 'reset-db-password' => P::ACTION_DATABASE_UPDATE, + 'delete-database' => P::ACTION_DATABASE_DELETE, + 'create-database' => P::ACTION_DATABASE_CREATE, + 'access-sftp' => P::ACTION_FILE_SFTP, + 'list-files' => P::ACTION_FILE_READ, + 'edit-files' => P::ACTION_FILE_READ_CONTENT, + 'save-files' => P::ACTION_FILE_UPDATE, + 'create-files' => P::ACTION_FILE_CREATE, + 'delete-files' => P::ACTION_FILE_DELETE, + 'compress-files' => P::ACTION_FILE_ARCHIVE, + 'list-schedules' => P::ACTION_SCHEDULE_READ, + 'view-schedule' => P::ACTION_SCHEDULE_READ, + 'edit-schedule' => P::ACTION_SCHEDULE_UPDATE, + 'create-schedule' => P::ACTION_SCHEDULE_CREATE, + 'delete-schedule' => P::ACTION_SCHEDULE_DELETE, + // Skipping these permissions as they are granted if you have more specific read/write permissions. + 'move-files' => null, + 'copy-files' => null, + 'decompress-files' => null, + 'upload-files' => null, + 'download-files' => null, + // These permissions do not exist in 1.0 + 'toggle-schedule' => null, + 'queue-schedule' => null, + ]; + + /** + * Run the migrations. + */ + public function up(): void + { + Schema::table('subusers', function (Blueprint $table) { + $table->json('permissions')->nullable()->after('server_id'); + }); + + $cursor = DB::table('permissions') + ->select(['subuser_id']) + ->when(DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME) === 'mysql', function ($query) { + $query->selectRaw('group_concat(permission) as permissions'); + }) + ->when(DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME) === 'pgsql', function ($query) { + $query->selectRaw("string_agg(permission, ',') as permissions"); + }) + ->from('permissions') + ->groupBy(['subuser_id']) + ->cursor(); + + DB::transaction(function () use (&$cursor) { + $cursor->each(function ($datum) { + $updated = Collection::make(explode(',', $datum->permissions)) + ->map(function ($value) { + return self::$permissionsMap[$value] ?? null; + })->filter(function ($value) { + return !is_null($value) && $value !== Permission::ACTION_WEBSOCKET_CONNECT; + }) + // All subusers get this permission, so make sure it gets pushed into the array. + ->merge([Permission::ACTION_WEBSOCKET_CONNECT]) + ->unique() + ->values() + ->toJson(); + + DB::update('UPDATE subusers SET permissions = ? WHERE id = ?', [$updated, $datum->subuser_id]); + }); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $flipped = array_flip(array_filter(self::$permissionsMap)); + + foreach (DB::select('SELECT id, permissions FROM subusers') as $datum) { + $values = []; + foreach (json_decode($datum->permissions, true) as $permission) { + $v = $flipped[$permission] ?? null; + if (!empty($v)) { + $values[] = $datum->id; + $values[] = $v; + } + } + + if (!empty($values)) { + $string = 'VALUES ' . implode(', ', array_fill(0, count($values) / 2, '(?, ?)')); + + DB::insert('INSERT INTO permissions(`subuser_id`, `permission`) ' . $string, $values); + } + } + + Schema::table('subusers', function (Blueprint $table) { + $table->dropColumn('permissions'); + }); + } +} diff --git a/database/migrations/2020_03_22_164814_drop_permissions_table.php b/database/migrations/2020_03_22_164814_drop_permissions_table.php new file mode 100644 index 000000000..030a8a6ba --- /dev/null +++ b/database/migrations/2020_03_22_164814_drop_permissions_table.php @@ -0,0 +1,30 @@ +increments('id'); + $table->unsignedInteger('subuser_id'); + $table->string('permission'); + + $table->foreign('subuser_id')->references('id')->on('subusers')->onDelete('cascade'); + }); + } +} diff --git a/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php new file mode 100644 index 000000000..d4c08c5e5 --- /dev/null +++ b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php @@ -0,0 +1,28 @@ +string('threads')->nullable()->after('cpu'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('threads'); + }); + } +} diff --git a/database/migrations/2020_04_03_230614_create_backups_table.php b/database/migrations/2020_04_03_230614_create_backups_table.php new file mode 100644 index 000000000..a8c28d96d --- /dev/null +++ b/database/migrations/2020_04_03_230614_create_backups_table.php @@ -0,0 +1,55 @@ +TABLE_NAME, $result->TABLE_NAME . '_plugin_bak'); + } + + Schema::create('backups', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedInteger('server_id'); + $table->char('uuid', 36); + $table->string('name'); + $table->text('ignored_files'); + $table->string('disk'); + $table->string('sha256_hash')->nullable(); + $table->integer('bytes')->default(0); + $table->timestamp('completed_at')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + $table->unique('uuid'); + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('backups'); + } +} diff --git a/database/migrations/2020_04_04_131016_add_table_server_transfers.php b/database/migrations/2020_04_04_131016_add_table_server_transfers.php new file mode 100644 index 000000000..c9f3e849a --- /dev/null +++ b/database/migrations/2020_04_04_131016_add_table_server_transfers.php @@ -0,0 +1,41 @@ +increments('id'); + $table->integer('server_id')->unsigned(); + $table->boolean('successful')->unsigned()->default(0); + $table->integer('old_node')->unsigned(); + $table->integer('new_node')->unsigned(); + $table->integer('old_allocation')->unsigned(); + $table->integer('new_allocation')->unsigned(); + $table->json('old_additional_allocations')->nullable(); + $table->json('new_additional_allocations')->nullable(); + $table->timestamps(); + + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('server_transfers'); + } +} diff --git a/database/migrations/2020_04_10_141024_store_node_tokens_as_encrypted_value.php b/database/migrations/2020_04_10_141024_store_node_tokens_as_encrypted_value.php new file mode 100644 index 000000000..a69dafc89 --- /dev/null +++ b/database/migrations/2020_04_10_141024_store_node_tokens_as_encrypted_value.php @@ -0,0 +1,85 @@ +dropUnique(['daemonSecret']); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->char('uuid', 36)->after('id'); + $table->char('daemon_token_id', 16)->after('upload_size'); + + $table->renameColumn('`daemonSecret`', 'daemon_token'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->text('daemon_token')->change(); + }); + + /** @var \Illuminate\Contracts\Encryption\Encrypter $encrypter */ + $encrypter = Container::getInstance()->make(Encrypter::class); + + foreach (DB::select('SELECT id, daemon_token FROM nodes') as $datum) { + DB::update('UPDATE nodes SET uuid = ?, daemon_token_id = ?, daemon_token = ? WHERE id = ?', [ + Uuid::uuid4()->toString(), + substr($datum->daemon_token, 0, 16), + $encrypter->encrypt(substr($datum->daemon_token, 16)), + $datum->id, + ]); + } + + Schema::table('nodes', function (Blueprint $table) { + $table->unique(['uuid']); + $table->unique(['daemon_token_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::transaction(function () { + /** @var \Illuminate\Contracts\Encryption\Encrypter $encrypter */ + $encrypter = Container::getInstance()->make(Encrypter::class); + + foreach (DB::select('SELECT id, daemon_token_id, daemon_token FROM nodes') as $datum) { + DB::update('UPDATE nodes SET daemon_token = ? WHERE id = ?', [ + $datum->daemon_token_id . $encrypter->decrypt($datum->daemon_token), + $datum->id, + ]); + } + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->dropUnique(['uuid']); + $table->dropUnique(['daemon_token_id']); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn(['uuid', 'daemon_token_id']); + $table->renameColumn('daemon_token', 'daemonSecret'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->string('daemonSecret', 36)->change(); + $table->unique(['daemonSecret']); + }); + } +} diff --git a/database/migrations/2020_04_17_203438_allow_nullable_descriptions.php b/database/migrations/2020_04_17_203438_allow_nullable_descriptions.php new file mode 100644 index 000000000..8c2c149cf --- /dev/null +++ b/database/migrations/2020_04_17_203438_allow_nullable_descriptions.php @@ -0,0 +1,52 @@ +text('description')->nullable()->change(); + }); + + Schema::table('nests', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->text('description')->nullable()->change(); + }); + + Schema::table('locations', function (Blueprint $table) { + $table->text('long')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->text('description')->nullable(false)->change(); + }); + + Schema::table('nests', function (Blueprint $table) { + $table->text('description')->nullable(false)->change(); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->text('description')->nullable(false)->change(); + }); + + Schema::table('locations', function (Blueprint $table) { + $table->text('long')->nullable(false)->change(); + }); + } +} diff --git a/database/migrations/2020_04_22_055500_add_max_connections_column.php b/database/migrations/2020_04_22_055500_add_max_connections_column.php new file mode 100644 index 000000000..57604117c --- /dev/null +++ b/database/migrations/2020_04_22_055500_add_max_connections_column.php @@ -0,0 +1,28 @@ +integer('max_connections')->nullable()->default(0)->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('databases', function (Blueprint $table) { + $table->dropColumn('max_connections'); + }); + } +} diff --git a/database/migrations/2020_04_26_111208_add_backup_limit_to_servers.php b/database/migrations/2020_04_26_111208_add_backup_limit_to_servers.php new file mode 100644 index 000000000..af1b72e64 --- /dev/null +++ b/database/migrations/2020_04_26_111208_add_backup_limit_to_servers.php @@ -0,0 +1,43 @@ +unsignedInteger('backup_limit')->default(0)->change(); + }); + } else { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedInteger('backup_limit')->default(0)->after('database_limit'); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('backup_limit'); + }); + } +} diff --git a/database/migrations/2020_05_20_234655_add_mounts_table.php b/database/migrations/2020_05_20_234655_add_mounts_table.php new file mode 100644 index 000000000..db3b409d8 --- /dev/null +++ b/database/migrations/2020_05_20_234655_add_mounts_table.php @@ -0,0 +1,49 @@ +increments('id')->unique(); + $table->char('uuid', 36)->unique(); + $table->string('name')->unique(); + $table->text('description')->nullable(); + $table->string('source'); + $table->string('target'); + $table->tinyInteger('read_only')->unsigned(); + $table->tinyInteger('user_mountable')->unsigned(); + }); + + Schema::create('egg_mount', function (Blueprint $table) { + $table->integer('egg_id'); + $table->integer('mount_id'); + + $table->unique(['egg_id', 'mount_id']); + }); + + Schema::create('mount_node', function (Blueprint $table) { + $table->integer('node_id'); + $table->integer('mount_id'); + + $table->unique(['node_id', 'mount_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('mount_node'); + Schema::dropIfExists('egg_mount'); + Schema::dropIfExists('mounts'); + } +} diff --git a/database/migrations/2020_05_21_192756_add_mount_server_table.php b/database/migrations/2020_05_21_192756_add_mount_server_table.php new file mode 100644 index 000000000..7d8e9438b --- /dev/null +++ b/database/migrations/2020_05_21_192756_add_mount_server_table.php @@ -0,0 +1,29 @@ +integer('server_id'); + $table->integer('mount_id'); + + $table->unique(['server_id', 'mount_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('mount_server'); + } +} diff --git a/database/migrations/2020_07_02_213612_create_user_recovery_tokens_table.php b/database/migrations/2020_07_02_213612_create_user_recovery_tokens_table.php new file mode 100644 index 000000000..11a6f513c --- /dev/null +++ b/database/migrations/2020_07_02_213612_create_user_recovery_tokens_table.php @@ -0,0 +1,31 @@ +id(); + $table->unsignedInteger('user_id'); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + + $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('recovery_tokens'); + } +} diff --git a/database/migrations/2020_07_09_201845_add_notes_column_for_allocations.php b/database/migrations/2020_07_09_201845_add_notes_column_for_allocations.php new file mode 100644 index 000000000..a93b48053 --- /dev/null +++ b/database/migrations/2020_07_09_201845_add_notes_column_for_allocations.php @@ -0,0 +1,28 @@ +string('notes')->nullable()->after('server_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('allocations', function (Blueprint $table) { + $table->dropColumn('notes'); + }); + } +} diff --git a/database/migrations/2020_08_20_205533_add_backup_state_column_to_backups.php b/database/migrations/2020_08_20_205533_add_backup_state_column_to_backups.php new file mode 100644 index 000000000..4ac99fedd --- /dev/null +++ b/database/migrations/2020_08_20_205533_add_backup_state_column_to_backups.php @@ -0,0 +1,28 @@ +boolean('is_successful')->after('uuid')->default(true); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->dropColumn('is_successful'); + }); + } +} diff --git a/database/migrations/2020_08_22_132500_update_bytes_to_unsigned_bigint.php b/database/migrations/2020_08_22_132500_update_bytes_to_unsigned_bigint.php new file mode 100644 index 000000000..5c4adca1a --- /dev/null +++ b/database/migrations/2020_08_22_132500_update_bytes_to_unsigned_bigint.php @@ -0,0 +1,28 @@ +unsignedBigInteger('bytes')->default(0)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->integer('bytes')->default(0)->change(); + }); + } +} diff --git a/database/migrations/2020_08_23_175331_modify_checksums_column_for_backups.php b/database/migrations/2020_08_23_175331_modify_checksums_column_for_backups.php new file mode 100644 index 000000000..763b20457 --- /dev/null +++ b/database/migrations/2020_08_23_175331_modify_checksums_column_for_backups.php @@ -0,0 +1,37 @@ +renameColumn('sha256_hash', 'checksum'); + }); + + Schema::table('backups', function (Blueprint $table) { + DB::update('UPDATE backups SET checksum = CONCAT(\'sha256:\', checksum)'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->renameColumn('checksum', 'sha256_hash'); + }); + + Schema::table('backups', function (Blueprint $table) { + DB::update('UPDATE backups SET sha256_hash = SUBSTRING(sha256_hash, 8)'); + }); + } +} diff --git a/database/migrations/2020_09_13_110007_drop_packs_from_servers.php b/database/migrations/2020_09_13_110007_drop_packs_from_servers.php new file mode 100644 index 000000000..53cba54f5 --- /dev/null +++ b/database/migrations/2020_09_13_110007_drop_packs_from_servers.php @@ -0,0 +1,30 @@ +dropForeign(['pack_id']); + $table->dropColumn('pack_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedInteger('pack_id')->after('egg_id')->nullable(); + $table->foreign('pack_id')->references('id')->on('packs'); + }); + } +} diff --git a/database/migrations/2020_09_13_110021_drop_packs_from_api_key_permissions.php b/database/migrations/2020_09_13_110021_drop_packs_from_api_key_permissions.php new file mode 100644 index 000000000..7c051db2c --- /dev/null +++ b/database/migrations/2020_09_13_110021_drop_packs_from_api_key_permissions.php @@ -0,0 +1,28 @@ +dropColumn('r_packs'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('api_keys', function (Blueprint $table) { + $table->unsignedTinyInteger('r_packs')->default(0); + }); + } +} diff --git a/database/migrations/2020_09_13_110047_drop_packs_table.php b/database/migrations/2020_09_13_110047_drop_packs_table.php new file mode 100644 index 000000000..58194b8fa --- /dev/null +++ b/database/migrations/2020_09_13_110047_drop_packs_table.php @@ -0,0 +1,39 @@ +increments('id'); + $table->unsignedInteger('egg_id'); + $table->char('uuid', 36)->unique(); + $table->string('name'); + $table->string('version'); + $table->text('description')->nullable(); + $table->tinyInteger('selectable')->default(1); + $table->tinyInteger('visible')->default(1); + $table->tinyInteger('locked')->default(0); + $table->timestamps(); + }); + + Schema::table('packs', function (Blueprint $table) { + $table->foreign('egg_id')->references('id')->on('eggs')->cascadeOnDelete(); + }); + } +} diff --git a/database/migrations/2020_09_13_113503_drop_daemon_key_table.php b/database/migrations/2020_09_13_113503_drop_daemon_key_table.php new file mode 100644 index 000000000..274f9fd97 --- /dev/null +++ b/database/migrations/2020_09_13_113503_drop_daemon_key_table.php @@ -0,0 +1,36 @@ +increments('id'); + $table->unsignedInteger('server_id'); + $table->unsignedInteger('user_id'); + $table->string('secret')->unique(); + $table->timestamp('expires_at'); + $table->timestamps(); + }); + + Schema::table('daemon_keys', function (Blueprint $table) { + $table->foreign('server_id')->references('id')->on('servers')->cascadeOnDelete(); + $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); + }); + } +} diff --git a/database/migrations/2020_09_25_021109_create_admin_roles_table.php b/database/migrations/2020_09_25_021109_create_admin_roles_table.php new file mode 100644 index 000000000..e67f75074 --- /dev/null +++ b/database/migrations/2020_09_25_021109_create_admin_roles_table.php @@ -0,0 +1,31 @@ +increments('id'); + $table->string('name', 64); + $table->string('description', 255)->nullable(); + $table->integer('sort_id'); + $table->json('permissions')->nullable(); + + $table->unique(['id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('admin_roles'); + } +}; diff --git a/database/migrations/2020_10_10_165437_change_unique_database_name_to_account_for_server.php b/database/migrations/2020_10_10_165437_change_unique_database_name_to_account_for_server.php new file mode 100644 index 000000000..6a277e449 --- /dev/null +++ b/database/migrations/2020_10_10_165437_change_unique_database_name_to_account_for_server.php @@ -0,0 +1,36 @@ +dropUnique(['database_host_id', 'database']); + }); + + Schema::table('databases', function (Blueprint $table) { + $table->unique(['database_host_id', 'server_id', 'database']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('databases', function (Blueprint $table) { + $table->dropUnique(['database_host_id', 'server_id', 'database']); + }); + + Schema::table('databases', function (Blueprint $table) { + $table->unique(['database_host_id', 'database']); + }); + } +} diff --git a/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php b/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php new file mode 100644 index 000000000..d0b3118e5 --- /dev/null +++ b/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php @@ -0,0 +1,31 @@ +string('name')->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('schedules', function (Blueprint $table) { + $table->string('name')->nullable()->change(); + }); + } +} diff --git a/database/migrations/2020_11_02_201014_add_features_column_to_eggs.php b/database/migrations/2020_11_02_201014_add_features_column_to_eggs.php new file mode 100644 index 000000000..e134fe9a6 --- /dev/null +++ b/database/migrations/2020_11_02_201014_add_features_column_to_eggs.php @@ -0,0 +1,28 @@ +json('features')->after('description')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('features'); + }); + } +} diff --git a/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php b/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php new file mode 100644 index 000000000..c7d2cff7f --- /dev/null +++ b/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php @@ -0,0 +1,58 @@ +json('docker_images')->after('docker_image')->nullable(); + $table->text('update_url')->after('docker_images')->nullable(); + }); + + switch (DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + DB::table('eggs')->update(['docker_images' => DB::raw('JSON_ARRAY(docker_image)')]); + break; + case 'pgsql': + DB::table('eggs')->update(['docker_images' => DB::raw('jsonb_build_array(docker_image)')]); + break; + } + + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('docker_image'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->text('docker_image')->after('docker_images'); + }); + + switch (DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + DB::table('eggs')->update(['docker_images' => DB::raw('JSON_UNQUOTE(JSON_EXTRACT(docker_images, "$[0]")')]); + break; + case 'pgsql': + DB::table('eggs')->update(['docker_images' => DB::raw('JSON_UNQUOTE(JSON_EXTRACT(docker_images, "$[0]")')]); + DB::table('eggs')->update(['docker_images' => DB::raw('docker_images->>0')]); + break; + } + + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('docker_images'); + $table->dropColumn('update_url'); + }); + } +} diff --git a/database/migrations/2020_12_14_013707_make_successful_nullable_in_server_transfers.php b/database/migrations/2020_12_14_013707_make_successful_nullable_in_server_transfers.php new file mode 100644 index 000000000..4c28b6c2b --- /dev/null +++ b/database/migrations/2020_12_14_013707_make_successful_nullable_in_server_transfers.php @@ -0,0 +1,28 @@ +boolean('successful')->nullable()->default(null)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('server_transfers', function (Blueprint $table) { + $table->boolean('successful')->default(0)->change(); + }); + } +} diff --git a/database/migrations/2020_12_17_014330_add_archived_field_to_server_transfers_table.php b/database/migrations/2020_12_17_014330_add_archived_field_to_server_transfers_table.php new file mode 100644 index 000000000..bc5d3356d --- /dev/null +++ b/database/migrations/2020_12_17_014330_add_archived_field_to_server_transfers_table.php @@ -0,0 +1,32 @@ +boolean('archived')->default(0)->after('new_additional_allocations'); + }); + + // Update archived to all be true on existing transfers. + DB::table('server_transfers')->where('successful', true)->update(['archived' => 1]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('server_transfers', function (Blueprint $table) { + $table->dropColumn('archived'); + }); + } +} diff --git a/database/migrations/2020_12_26_184914_add_upload_id_column_to_backups_table.php b/database/migrations/2020_12_26_184914_add_upload_id_column_to_backups_table.php new file mode 100644 index 000000000..771e06ab1 --- /dev/null +++ b/database/migrations/2020_12_26_184914_add_upload_id_column_to_backups_table.php @@ -0,0 +1,28 @@ +text('upload_id')->nullable()->after('uuid'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->dropColumn('upload_id'); + }); + } +} diff --git a/database/migrations/2021_01_10_153937_add_file_denylist_to_egg_configs.php b/database/migrations/2021_01_10_153937_add_file_denylist_to_egg_configs.php new file mode 100644 index 000000000..4a956625e --- /dev/null +++ b/database/migrations/2021_01_10_153937_add_file_denylist_to_egg_configs.php @@ -0,0 +1,28 @@ +text('file_denylist')->after('docker_images'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('file_denylist'); + }); + } +} diff --git a/database/migrations/2021_01_13_013420_add_cron_month.php b/database/migrations/2021_01_13_013420_add_cron_month.php new file mode 100644 index 000000000..da0bf8841 --- /dev/null +++ b/database/migrations/2021_01_13_013420_add_cron_month.php @@ -0,0 +1,28 @@ +string('cron_month')->after('cron_day_of_week'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('schedules', function (Blueprint $table) { + $table->dropColumn('cron_month'); + }); + } +} diff --git a/database/migrations/2021_01_16_201057_add_admin_role_id_column_to_users_table.php b/database/migrations/2021_01_16_201057_add_admin_role_id_column_to_users_table.php new file mode 100644 index 000000000..641dc62db --- /dev/null +++ b/database/migrations/2021_01_16_201057_add_admin_role_id_column_to_users_table.php @@ -0,0 +1,30 @@ +integer('admin_role_id')->nullable()->unsigned()->after('language'); + $table->index('admin_role_id'); + $table->foreign('admin_role_id')->references('id')->on('admin_roles')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeign(['admin_role_id']); + $table->dropColumn('admin_role_id'); + }); + } +}; diff --git a/database/migrations/2021_01_17_102401_create_audit_logs_table.php b/database/migrations/2021_01_17_102401_create_audit_logs_table.php new file mode 100644 index 000000000..d0fddf801 --- /dev/null +++ b/database/migrations/2021_01_17_102401_create_audit_logs_table.php @@ -0,0 +1,38 @@ +id(); + $table->char('uuid', 36); + $table->boolean('is_system')->default(false); + $table->unsignedInteger('user_id')->nullable(); + $table->unsignedInteger('server_id')->nullable(); + $table->string('action'); + $table->string('subaction')->nullable(); + $table->json('device'); + $table->json('metadata'); + $table->timestamp('created_at', 0); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('set null'); + $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('audit_logs'); + } +} diff --git a/database/migrations/2021_01_17_152623_add_generic_server_status_column.php b/database/migrations/2021_01_17_152623_add_generic_server_status_column.php new file mode 100644 index 000000000..f8c9dec2c --- /dev/null +++ b/database/migrations/2021_01_17_152623_add_generic_server_status_column.php @@ -0,0 +1,47 @@ +string('status')->nullable()->after('description'); + }); + + DB::table('servers')->where('suspended', 1)->update(['status' => 'suspended']); + DB::table('servers')->where('installed', 1)->update(['status' => 'installing']); + DB::table('servers')->where('installed', 1)->update(['status' => 'install_failed']); + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('suspended'); + $table->dropColumn('installed'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->unsignedTinyInteger('suspended')->default(0); + $table->unsignedTinyInteger('installed')->default(0); + }); + + DB::table('servers')->where('status', 'suspended')->update(['suspended' => 1]); + DB::table('servers')->whereNull('status')->update(['installed' => 1]); + DB::table('servers')->where('status', 'install_failed')->update(['installed' => 2]); + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('status'); + }); + } +} diff --git a/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php b/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php new file mode 100644 index 000000000..7c2c5fa06 --- /dev/null +++ b/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php @@ -0,0 +1,36 @@ +dropColumn('file_denylist'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->json('file_denylist')->nullable()->after('docker_images'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('file_denylist'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->text('file_denylist')->after('docker_images'); + }); + } +} diff --git a/database/migrations/2021_01_30_193343_add_database_host_id_column_to_nodes_table.php b/database/migrations/2021_01_30_193343_add_database_host_id_column_to_nodes_table.php new file mode 100644 index 000000000..04ce1640e --- /dev/null +++ b/database/migrations/2021_01_30_193343_add_database_host_id_column_to_nodes_table.php @@ -0,0 +1,41 @@ +dropForeign(['node_id']); + $table->dropColumn('node_id'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->integer('database_host_id')->nullable()->unsigned()->after('location_id'); + $table->index('database_host_id'); + $table->foreign('database_host_id')->references('id')->on('database_hosts')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropForeign(['database_host_id']); + $table->dropColumn('database_host_id'); + }); + + Schema::table('database_hosts', function (Blueprint $table) { + $table->integer('node_id')->nullable()->unsigned()->after('max_databases'); + $table->index('node_id'); + $table->foreign('node_id')->references('id')->on('nodes'); + }); + } +}; diff --git a/database/migrations/2021_02_23_205021_add_index_for_server_and_action.php b/database/migrations/2021_02_23_205021_add_index_for_server_and_action.php new file mode 100644 index 000000000..1fd63559c --- /dev/null +++ b/database/migrations/2021_02_23_205021_add_index_for_server_and_action.php @@ -0,0 +1,35 @@ +index(['action', 'server_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('audit_logs', function (Blueprint $table) { + $table->dropIndex(['action', 'server_id']); + }); + } +} diff --git a/database/migrations/2021_02_23_212657_make_sftp_port_unsigned_int.php b/database/migrations/2021_02_23_212657_make_sftp_port_unsigned_int.php new file mode 100644 index 000000000..b9196a0ae --- /dev/null +++ b/database/migrations/2021_02_23_212657_make_sftp_port_unsigned_int.php @@ -0,0 +1,28 @@ +unsignedSmallInteger('daemonSFTP')->default(2022)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('nodes', function (Blueprint $table) { + $table->smallInteger('daemonSFTP')->default(2022)->change(); + }); + } +} diff --git a/database/migrations/2021_02_23_212658_change_port_columns_on_nodes_table.php b/database/migrations/2021_02_23_212658_change_port_columns_on_nodes_table.php new file mode 100644 index 000000000..8eeef0927 --- /dev/null +++ b/database/migrations/2021_02_23_212658_change_port_columns_on_nodes_table.php @@ -0,0 +1,58 @@ +renameColumn('`daemonListen`', 'listen_port_http'); + $table->renameColumn('`daemonSFTP`', 'listen_port_sftp'); + $table->renameColumn('`daemonBase`', 'daemon_base'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->integer('listen_port_http')->unsigned()->default(8080)->after('fqdn')->change(); + $table->integer('listen_port_sftp')->unsigned()->default(2022)->after('listen_port_http')->change(); + + $table->integer('public_port_http')->unsigned()->default(8080)->after('listen_port_sftp'); + $table->integer('public_port_sftp')->unsigned()->default(2022)->after('public_port_http'); + }); + + DB::transaction(function () { + foreach (DB::select('SELECT id, listen_port_http, listen_port_sftp FROM nodes') as $datum) { + DB::update('UPDATE nodes SET public_port_http = ?, public_port_sftp = ? WHERE id = ?', [ + $datum->listen_port_http, + $datum->listen_port_sftp, + $datum->id, + ]); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('nodes', function (Blueprint $table) { + $table->renameColumn('listen_port_http', '`daemonListen`'); + $table->renameColumn('listen_port_sftp', '`daemonSFTP`'); + $table->renameColumn('daemon_base', '`daemonBase`'); + + $table->dropColumn('public_port_http'); + $table->dropColumn('public_port_sftp'); + }); + + Schema::table('nodes', function (Blueprint $table) { + $table->smallInteger('daemonListen')->unsigned()->default(8080)->after('daemon_token')->change(); + $table->smallInteger('daemonSFTP')->unsigned()->default(2022)->after('daemonListen')->change(); + }); + } +}; diff --git a/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php b/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php new file mode 100644 index 000000000..61abdbd6c --- /dev/null +++ b/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php @@ -0,0 +1,23 @@ +where('cron_month', '')->update(['cron_month' => '*']); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // No down function. + } +} diff --git a/database/migrations/2021_05_01_092457_add_continue_on_failure_option_to_tasks.php b/database/migrations/2021_05_01_092457_add_continue_on_failure_option_to_tasks.php new file mode 100644 index 000000000..079045496 --- /dev/null +++ b/database/migrations/2021_05_01_092457_add_continue_on_failure_option_to_tasks.php @@ -0,0 +1,28 @@ +unsignedTinyInteger('continue_on_failure')->after('is_queued')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('tasks', function (Blueprint $table) { + $table->dropColumn('continue_on_failure'); + }); + } +} diff --git a/database/migrations/2021_05_01_092523_add_only_run_when_server_online_option_to_schedules.php b/database/migrations/2021_05_01_092523_add_only_run_when_server_online_option_to_schedules.php new file mode 100644 index 000000000..a0143fdd6 --- /dev/null +++ b/database/migrations/2021_05_01_092523_add_only_run_when_server_online_option_to_schedules.php @@ -0,0 +1,28 @@ +unsignedTinyInteger('only_when_online')->after('is_processing')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('schedules', function (Blueprint $table) { + $table->dropColumn('only_when_online'); + }); + } +} diff --git a/database/migrations/2021_05_03_201016_add_support_for_locking_a_backup.php b/database/migrations/2021_05_03_201016_add_support_for_locking_a_backup.php new file mode 100644 index 000000000..3296e3e8f --- /dev/null +++ b/database/migrations/2021_05_03_201016_add_support_for_locking_a_backup.php @@ -0,0 +1,28 @@ +unsignedTinyInteger('is_locked')->after('is_successful')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->dropColumn('is_locked'); + }); + } +} diff --git a/database/migrations/2021_07_12_013420_remove_userinteraction.php b/database/migrations/2021_07_12_013420_remove_userinteraction.php new file mode 100644 index 000000000..33b4d5d3b --- /dev/null +++ b/database/migrations/2021_07_12_013420_remove_userinteraction.php @@ -0,0 +1,47 @@ +getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + DB::table('eggs')->update([ + 'config_startup' => DB::raw('JSON_REMOVE(config_startup, \'$.userInteraction\')'), + ]); + break; + case 'pgsql': + DB::table('eggs')->update([ + 'config_startup' => DB::raw('config_startup::jsonb - \'userInteraction\''), + ]); + break; + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Add blank User Interaction array back to startup config + switch (DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + DB::table('eggs')->update([ + 'config_startup' => DB::raw('JSON_SET(config_startup, \'$.userInteraction\', JSON_ARRAY())'), + ]); + break; + case 'pgsql': + DB::table('eggs')->update([ + 'config_startup' => DB::raw('jsonb_set(config_startup::jsonb, \'$.userInteraction\', jsonb_build_array())'), + ]); + break; + } + } +} diff --git a/database/migrations/2021_07_17_211512_create_user_ssh_keys_table.php b/database/migrations/2021_07_17_211512_create_user_ssh_keys_table.php new file mode 100644 index 000000000..a33bb4d31 --- /dev/null +++ b/database/migrations/2021_07_17_211512_create_user_ssh_keys_table.php @@ -0,0 +1,34 @@ +increments('id'); + $table->unsignedInteger('user_id'); + $table->string('name'); + $table->string('fingerprint'); + $table->text('public_key'); + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_ssh_keys'); + } +} diff --git a/database/migrations/2021_07_29_032255_yeet_names_from_users_table.php b/database/migrations/2021_07_29_032255_yeet_names_from_users_table.php new file mode 100644 index 000000000..0a22a2059 --- /dev/null +++ b/database/migrations/2021_07_29_032255_yeet_names_from_users_table.php @@ -0,0 +1,28 @@ +dropColumn(['name_first', 'name_last']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->string('name_first')->after('email')->nullable(); + $table->string('name_last')->after('name_first')->nullable(); + }); + } +}; diff --git a/database/migrations/2021_08_03_210600_change_successful_field_to_default_to_false_on_backups_table.php b/database/migrations/2021_08_03_210600_change_successful_field_to_default_to_false_on_backups_table.php new file mode 100644 index 000000000..b9284eb0c --- /dev/null +++ b/database/migrations/2021_08_03_210600_change_successful_field_to_default_to_false_on_backups_table.php @@ -0,0 +1,34 @@ +boolean('is_successful')->after('uuid')->default(false)->change(); + }); + + // Convert currently processing backups to the new format so things don't break. + DB::table('backups')->select('id')->where('is_successful', 1)->whereNull('completed_at')->update([ + 'is_successful' => 0, + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->boolean('is_successful')->after('uuid')->default(true)->change(); + }); + } +} diff --git a/database/migrations/2021_08_21_175111_add_foreign_keys_to_mount_node_table.php b/database/migrations/2021_08_21_175111_add_foreign_keys_to_mount_node_table.php new file mode 100644 index 000000000..5210f60d2 --- /dev/null +++ b/database/migrations/2021_08_21_175111_add_foreign_keys_to_mount_node_table.php @@ -0,0 +1,55 @@ +unsignedInteger('node_id')->change(); + $table->unsignedInteger('mount_id')->change(); + }); + + // Fetch an array of node and mount ids to check relations against. + $nodes = DB::table('nodes')->select('id')->pluck('id')->toArray(); + $mounts = DB::table('mounts')->select('id')->pluck('id')->toArray(); + + // Drop any relations that are missing a node or mount. + DB::table('mount_node') + ->select('node_id', 'mount_id') + ->whereNotIn('node_id', $nodes) + ->orWhereNotIn('mount_id', $mounts) + ->delete(); + + Schema::table('mount_node', function (Blueprint $table) { + $table->foreign('node_id') + ->references('id') + ->on('nodes') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + $table->foreign('mount_id')->references('id') + ->on('mounts') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('mount_node', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->dropForeign(['mount_id']); + }); + } +} diff --git a/database/migrations/2021_08_21_175118_add_foreign_keys_to_mount_server_table.php b/database/migrations/2021_08_21_175118_add_foreign_keys_to_mount_server_table.php new file mode 100644 index 000000000..f564ec491 --- /dev/null +++ b/database/migrations/2021_08_21_175118_add_foreign_keys_to_mount_server_table.php @@ -0,0 +1,54 @@ +unsignedInteger('server_id')->change(); + $table->unsignedInteger('mount_id')->change(); + }); + + // Fetch an array of node and mount ids to check relations against. + $servers = DB::table('servers')->select('id')->pluck('id')->toArray(); + $mounts = DB::table('mounts')->select('id')->pluck('id')->toArray(); + + // Drop any relations that are missing a server or mount. + DB::table('mount_server') + ->select('server_id', 'mount_id') + ->whereNotIn('server_id', $servers) + ->orWhereNotIn('mount_id', $mounts) + ->delete(); + + Schema::table('mount_server', function (Blueprint $table) { + $table->foreign('server_id') + ->references('id') + ->on('servers') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + $table->foreign('mount_id')->references('id') + ->on('mounts') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('mount_server', function (Blueprint $table) { + $table->dropForeign(['server_id']); + $table->dropForeign(['mount_id']); + }); + } +} diff --git a/database/migrations/2021_08_21_180921_add_foreign_keys_to_egg_mount_table.php b/database/migrations/2021_08_21_180921_add_foreign_keys_to_egg_mount_table.php new file mode 100644 index 000000000..11285a5c5 --- /dev/null +++ b/database/migrations/2021_08_21_180921_add_foreign_keys_to_egg_mount_table.php @@ -0,0 +1,54 @@ +unsignedInteger('egg_id')->change(); + $table->unsignedInteger('mount_id')->change(); + }); + + // Fetch an array of node and mount ids to check relations against. + $eggs = DB::table('eggs')->select('id')->pluck('id')->toArray(); + $mounts = DB::table('mounts')->select('id')->pluck('id')->toArray(); + + // Drop any relations that are missing an egg or mount. + DB::table('egg_mount') + ->select('egg_id', 'mount_id') + ->whereNotIn('egg_id', $eggs) + ->orWhereNotIn('mount_id', $mounts) + ->delete(); + + Schema::table('egg_mount', function (Blueprint $table) { + $table->foreign('egg_id') + ->references('id') + ->on('eggs') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + $table->foreign('mount_id')->references('id') + ->on('mounts') + ->cascadeOnDelete() + ->cascadeOnUpdate(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('egg_mount', function (Blueprint $table) { + $table->dropForeign(['egg_id']); + $table->dropForeign(['mount_id']); + }); + } +} diff --git a/database/migrations/2021_10_23_185304_drop_config_logs_column_from_eggs_table.php b/database/migrations/2021_10_23_185304_drop_config_logs_column_from_eggs_table.php new file mode 100644 index 000000000..b78a9698c --- /dev/null +++ b/database/migrations/2021_10_23_185304_drop_config_logs_column_from_eggs_table.php @@ -0,0 +1,27 @@ +dropColumn('config_logs'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->text('config_logs')->nullable()->after('docker_image'); + }); + } +}; diff --git a/database/migrations/2021_10_23_202643_update_default_values_for_eggs.php b/database/migrations/2021_10_23_202643_update_default_values_for_eggs.php new file mode 100644 index 000000000..fee18b92b --- /dev/null +++ b/database/migrations/2021_10_23_202643_update_default_values_for_eggs.php @@ -0,0 +1,28 @@ +string('script_container')->default('ghcr.io/pterodactyl/installers:alpine')->after('startup')->change(); + $table->string('script_entry')->default('/bin/ash')->after('copy_script_from')->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + // You are stuck with the new values because I am too lazy to revert them :) + }); + } +}; diff --git a/database/migrations/2021_11_01_180130_make_startup_field_nullable_on_servers_table.php b/database/migrations/2021_11_01_180130_make_startup_field_nullable_on_servers_table.php new file mode 100644 index 000000000..3795eb752 --- /dev/null +++ b/database/migrations/2021_11_01_180130_make_startup_field_nullable_on_servers_table.php @@ -0,0 +1,27 @@ +text('startup')->default(null)->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->text('startup')->change(); + }); + } +}; diff --git a/database/migrations/2022_01_25_030847_drop_google_analytics.php b/database/migrations/2022_01_25_030847_drop_google_analytics.php new file mode 100644 index 000000000..0d65e5d4d --- /dev/null +++ b/database/migrations/2022_01_25_030847_drop_google_analytics.php @@ -0,0 +1,25 @@ +where('key', 'settings::app:analytics')->delete(); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::table('settings')->insert([ + 'key' => 'settings::app:analytics', + ]); + } +} diff --git a/database/migrations/2022_05_07_165334_migrate_egg_images_array_to_new_format.php b/database/migrations/2022_05_07_165334_migrate_egg_images_array_to_new_format.php new file mode 100644 index 000000000..057ce9d74 --- /dev/null +++ b/database/migrations/2022_05_07_165334_migrate_egg_images_array_to_new_format.php @@ -0,0 +1,38 @@ + value pairings to support naming the + * images provided. + */ + public function up(): void + { + DB::table('eggs')->select(['id', 'docker_images'])->cursor()->each(function ($egg) { + $images = is_null($egg->docker_images) ? [] : json_decode($egg->docker_images, true, 512, JSON_THROW_ON_ERROR); + + $results = []; + foreach ($images as $key => $value) { + $results[is_int($key) ? $value : $key] = $value; + } + + DB::table('eggs')->where('id', $egg->id)->update(['docker_images' => $results]); + }); + } + + /** + * Reverse the migrations. This just keeps the values from the docker images array. + */ + public function down(): void + { + DB::table('eggs')->select(['id', 'docker_images'])->cursor()->each(function ($egg) { + DB::table('eggs')->where('id', $egg->id)->update([ + 'docker_images' => array_values(json_decode($egg->docker_images, true, 512, JSON_THROW_ON_ERROR)), + ]); + }); + } +} diff --git a/database/migrations/2022_05_28_135717_create_activity_logs_table.php b/database/migrations/2022_05_28_135717_create_activity_logs_table.php new file mode 100644 index 000000000..4c4f05f1f --- /dev/null +++ b/database/migrations/2022_05_28_135717_create_activity_logs_table.php @@ -0,0 +1,33 @@ +id(); + $table->uuid('batch')->nullable(); + $table->string('event')->index(); + $table->string('ip'); + $table->text('description')->nullable(); + $table->nullableNumericMorphs('actor'); + $table->json('properties'); + $table->timestamp('timestamp')->useCurrent()->onUpdate(null); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('activity_logs'); + } +} diff --git a/database/migrations/2022_05_29_140349_create_activity_log_actors_table.php b/database/migrations/2022_05_29_140349_create_activity_log_actors_table.php new file mode 100644 index 000000000..c9997ac41 --- /dev/null +++ b/database/migrations/2022_05_29_140349_create_activity_log_actors_table.php @@ -0,0 +1,28 @@ +id(); + $table->foreignId('activity_log_id')->references('id')->on('activity_logs')->cascadeOnDelete(); + $table->numericMorphs('subject'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('activity_log_subjects'); + } +} diff --git a/database/migrations/2022_06_18_112822_track_api_key_usage_for_activity_events.php b/database/migrations/2022_06_18_112822_track_api_key_usage_for_activity_events.php new file mode 100644 index 000000000..f13878637 --- /dev/null +++ b/database/migrations/2022_06_18_112822_track_api_key_usage_for_activity_events.php @@ -0,0 +1,27 @@ +unsignedInteger('api_key_id')->nullable()->after('actor_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('activity_logs', function (Blueprint $table) { + $table->dropColumn('api_key_id'); + }); + } +}; diff --git a/database/migrations/2022_08_16_214400_add_force_outgoing_ip_column_to_eggs_table.php b/database/migrations/2022_08_16_214400_add_force_outgoing_ip_column_to_eggs_table.php new file mode 100644 index 000000000..c5b30a49a --- /dev/null +++ b/database/migrations/2022_08_16_214400_add_force_outgoing_ip_column_to_eggs_table.php @@ -0,0 +1,28 @@ +boolean('force_outgoing_ip')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('force_outgoing_ip'); + }); + } +} diff --git a/database/migrations/2022_08_16_230204_add_installed_at_column_to_servers_table.php b/database/migrations/2022_08_16_230204_add_installed_at_column_to_servers_table.php new file mode 100644 index 000000000..541117a3a --- /dev/null +++ b/database/migrations/2022_08_16_230204_add_installed_at_column_to_servers_table.php @@ -0,0 +1,28 @@ +timestamp('installed_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('installed_at'); + }); + } +} diff --git a/database/migrations/2022_11_01_163744_make_ignored_files_column_nullable_on_backups_table.php b/database/migrations/2022_11_01_163744_make_ignored_files_column_nullable_on_backups_table.php new file mode 100644 index 000000000..ed36c6468 --- /dev/null +++ b/database/migrations/2022_11_01_163744_make_ignored_files_column_nullable_on_backups_table.php @@ -0,0 +1,27 @@ +text('ignored_files')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('backups', function (Blueprint $table) { + $table->text('ignored_files')->change(); + }); + } +}; diff --git a/database/migrations/2022_11_01_165830_fix_language_column_type_on_users_table.php b/database/migrations/2022_11_01_165830_fix_language_column_type_on_users_table.php new file mode 100644 index 000000000..0268810fd --- /dev/null +++ b/database/migrations/2022_11_01_165830_fix_language_column_type_on_users_table.php @@ -0,0 +1,36 @@ +getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + DB::statement('ALTER TABLE users MODIFY COLUMN language VARCHAR(5)'); + break; + case 'pgsql': + DB::statement('ALTER TABLE users ALTER COLUMN language TYPE varchar(5)'); + break; + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + switch (DB::getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME)) { + case 'mysql': + DB::statement('ALTER TABLE users MODIFY COLUMN language CHAR(5)'); + break; + case 'pgsql': + DB::statement('ALTER TABLE users ALTER COLUMN language TYPE CHAR(5)'); + break; + } + } +}; diff --git a/database/migrations/2022_12_12_213937_update_mail_settings_to_new_format.php b/database/migrations/2022_12_12_213937_update_mail_settings_to_new_format.php new file mode 100644 index 000000000..97b651033 --- /dev/null +++ b/database/migrations/2022_12_12_213937_update_mail_settings_to_new_format.php @@ -0,0 +1,77 @@ +keys, 0); + $oldSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $oldKeys)); + + // Gets the second column in our key table and gets all matching settings. + $newKeys = array_column($this->keys, 1); + $newSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $newKeys)); + + // Map all the old settings to their new key. + $oldSettings->map(function (Setting $setting) use ($oldKeys) { + $row = array_search($setting->key, $oldKeys, true); + $setting->key = $this->keys[$row][1]; + + return $setting; + // Check if any settings with the new key already exist. + })->filter(function (Setting $setting) use ($newSettings) { + if ($newSettings->contains('key', $setting->key)) { + return false; + } + + return true; + // Update the settings to use their new keys if they don't already exist. + })->each(fn (Setting $setting) => $setting->save()); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::transaction(function () { + $settings = Setting::all(); + + // Gets the second column in our key table and gets all matching settings. + $newKeys = array_column($this->keys, 0); + $newSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $newKeys)); + + // Delete all settings that already have the new key. + $newSettings->each(fn (Setting $setting) => $setting->delete()); + + // Gets the first column in our key table and gets all matching settings. + $oldKeys = array_column($this->keys, 1); + $oldSettings = $settings->filter(fn (Setting $setting) => in_array($setting->key, $oldKeys)); + + // Map all the old settings to their new key. + $oldSettings->map(function (Setting $setting) use ($oldKeys) { + $row = array_search($setting->key, $oldKeys, true); + $setting->key = $this->keys[$row][0]; + + return $setting; + // Update the settings to use their new keys if they don't already exist. + })->each(fn (Setting $setting) => $setting->save()); + }); + } +}; diff --git a/database/migrations/2023_01_17_185605_rename_oom_disabled_column_to_oom_killer.php b/database/migrations/2023_01_17_185605_rename_oom_disabled_column_to_oom_killer.php new file mode 100644 index 000000000..fb6dfe8e0 --- /dev/null +++ b/database/migrations/2023_01_17_185605_rename_oom_disabled_column_to_oom_killer.php @@ -0,0 +1,44 @@ +tinyInteger('oom_killer')->unsigned()->default(1)->after('oom_disabled'); + }); + + DB::table('servers')->select(['id', 'oom_disabled'])->cursor()->each(function ($server) { + DB::table('servers')->where('id', $server->id)->update(['oom_killer' => !$server->oom_disabled]); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('oom_disabled'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->tinyInteger('oom_disabled')->unsigned()->default(0)->after('oom_killer'); + }); + + DB::table('servers')->select(['id', 'oom_killer'])->cursor()->each(function ($server) { + DB::table('servers')->where('id', $server->id)->update(['oom_disabled' => !$server->oom_killer]); + }); + + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('oom_killer'); + }); + } +}; diff --git a/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php b/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php new file mode 100644 index 000000000..e3482e278 --- /dev/null +++ b/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php @@ -0,0 +1,34 @@ +string('uuid')->after('id')->nullable()->unique(); + }); + + DB::table('failed_jobs')->whereNull('uuid')->cursor()->each(function ($job) { + DB::table('failed_jobs') + ->where('id', $job->id) + ->update(['uuid' => (string) Illuminate\Support\Str::uuid()]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('failed_jobs', function (Blueprint $table) { + $table->dropColumn('uuid'); + }); + } +}; diff --git a/database/migrations/2023_02_23_191004_add_expires_at_column_to_api_keys_table.php b/database/migrations/2023_02_23_191004_add_expires_at_column_to_api_keys_table.php new file mode 100644 index 000000000..49e19bfed --- /dev/null +++ b/database/migrations/2023_02_23_191004_add_expires_at_column_to_api_keys_table.php @@ -0,0 +1,27 @@ +timestamp('expires_at')->nullable()->after('last_used_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('expires_at'); + }); + } +}; diff --git a/database/seeds/.gitkeep b/database/seeds/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/database/seeds/EggSeeder.php b/database/seeds/EggSeeder.php deleted file mode 100644 index 245d74ac3..000000000 --- a/database/seeds/EggSeeder.php +++ /dev/null @@ -1,143 +0,0 @@ -filesystem = $filesystem; - $this->importerService = $importerService; - $this->repository = $repository; - $this->updateImporterService = $updateImporterService; - $this->nestRepository = $nestRepository; - } - - /** - * Run the egg seeder. - */ - public function run() - { - $this->getEggsToImport()->each(function ($nest) { - $this->parseEggFiles($this->findMatchingNest($nest)); - }); - } - - /** - * Return a list of eggs to import. - * - * @return \Illuminate\Support\Collection - */ - protected function getEggsToImport(): Collection - { - return collect([ - 'Minecraft', - 'Source Engine', - 'Voice Servers', - 'Rust', - ]); - } - - /** - * Find the nest that these eggs should be attached to. - * - * @param string $nestName - * @return \Pterodactyl\Models\Nest - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - private function findMatchingNest(string $nestName): Nest - { - return $this->nestRepository->findFirstWhere([ - ['author', '=', 'support@pterodactyl.io'], - ['name', '=', $nestName], - ]); - } - - /** - * Loop through the list of egg files and import them. - * - * @param \Pterodactyl\Models\Nest $nest - */ - private function parseEggFiles(Nest $nest) - { - $files = $this->filesystem->allFiles(database_path('seeds/eggs/' . kebab_case($nest->name))); - - $this->command->alert('Updating Eggs for Nest: ' . $nest->name); - collect($files)->each(function ($file) use ($nest) { - /* @var \Symfony\Component\Finder\SplFileInfo $file */ - $decoded = json_decode($file->getContents()); - if (json_last_error() !== JSON_ERROR_NONE) { - return $this->command->error('JSON decode exception for ' . $file->getFilename() . ': ' . json_last_error_msg()); - } - - $file = new UploadedFile($file->getPathname(), $file->getFilename(), 'application/json', $file->getSize()); - - try { - $egg = $this->repository->setColumns('id')->findFirstWhere([ - ['author', '=', $decoded->author], - ['name', '=', $decoded->name], - ['nest_id', '=', $nest->id], - ]); - - $this->updateImporterService->handle($egg->id, $file); - - return $this->command->info('Updated ' . $decoded->name); - } catch (RecordNotFoundException $exception) { - $this->importerService->handle($file, $nest->id); - - return $this->command->comment('Created ' . $decoded->name); - } - }); - - $this->command->line(''); - } -} diff --git a/database/seeds/eggs/minecraft/egg-bungeecord.json b/database/seeds/eggs/minecraft/egg-bungeecord.json deleted file mode 100644 index d527024f3..000000000 --- a/database/seeds/eggs/minecraft/egg-bungeecord.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2017-11-03T22:15:10-05:00", - "name": "Bungeecord", - "author": "support@pterodactyl.io", - "description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.", - "image": "quay.io\/pterodactyl\/core:java", - "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", - "config": { - "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_enabled\": true,\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"127.0.0.1\": \"{{config.docker.interface}}\",\r\n \"localhost\": \"{{config.docker.interface}}\"\r\n }\r\n }\r\n }\r\n}", - "startup": "{\r\n \"done\": \"Listening on \",\r\n \"userInteraction\": [\r\n \"Listening on \/0.0.0.0:25577\"\r\n ]\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"proxy.log.0\"\r\n}", - "stop": "end" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\n# Bungeecord Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl\n\ncd \/mnt\/server\n\nif [ -z \"${BUNGEE_VERSION}\" ] || [ \"${BUNGEE_VERSION}\" == \"latest\" ]; then\n BUNGEE_VERSION=\"lastStableBuild\"\nfi\n\ncurl -o ${SERVER_JARFILE} https:\/\/ci.md-5.net\/job\/BungeeCord\/${BUNGEE_VERSION}\/artifact\/bootstrap\/target\/BungeeCord.jar", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Bungeecord Version", - "description": "The version of Bungeecord to download and use.", - "env_variable": "BUNGEE_VERSION", - "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|alpha_num|between:1,6" - }, - { - "name": "Bungeecord Jar File", - "description": "The name of the Jarfile to use when running Bungeecord.", - "env_variable": "SERVER_JARFILE", - "default_value": "bungeecord.jar", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" - } - ] -} diff --git a/database/seeds/eggs/minecraft/egg-forge-minecraft.json b/database/seeds/eggs/minecraft/egg-forge-minecraft.json deleted file mode 100644 index 2e8b43bfe..000000000 --- a/database/seeds/eggs/minecraft/egg-forge-minecraft.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2019-02-07T07:47:35-05:00", - "name": "Forge Minecraft", - "author": "support@pterodactyl.io", - "description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.", - "image": "quay.io\/pterodactyl\/core:java", - "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", - "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", - "startup": "{\r\n \"done\": \")! For help, type \",\r\n \"userInteraction\": [\r\n \"Go to eula.txt for more info.\"\r\n ]\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "stop" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\nif [ -z \"$MC_VERSION\" ] || [ \"$MC_VERSION\" == \"latest\" ]; then\r\n FORGE_VERSION=$(echo $(curl -sSl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A2 Latest | grep small) | grep -o -e '[1].[0-9]*.[0-9]* - [0-9]*.[0-9]*.[0-9]*.[0-9]*' | sed 's\/ \/\/g')\r\nelse\r\n FORGE_VERSION=$(echo $(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/index_$MC_VERSION.html | grep -A2 Latest | grep small) | grep -o -e '[1].[0-9]*.[0-9]* - [0-9]*.[0-9]*.[0-9]*.[0-9]*' | sed 's\/ \/\/g')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"\\nDownloading Forge Version $FORGE_VERSION\\n\"\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$FORGE_VERSION\/forge-$FORGE_VERSION-installer.jar -o installer.jar\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$FORGE_VERSION\/forge-$FORGE_VERSION-universal.jar -o $SERVER_JARFILE\r\n\r\necho -e \"\\nInstalling forge server usint the installer jar file.\\n\"\r\njava -jar installer.jar --installServer\r\n\r\necho -e \"\\nDeleting installer jar file and cleaning up.\\n\"\r\nrm -rf installer.jar", - "container": "openjdk:8-alpine", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Server Jar File", - "description": "The name of the Jarfile to use when running Forge Mod.", - "env_variable": "SERVER_JARFILE", - "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" - }, - { - "name": "Minecraft version", - "description": "The version of minecraft that you want to run. Example (1.10.2).", - "env_variable": "MC_VERSION", - "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:20" - } - ] -} \ No newline at end of file diff --git a/database/seeds/eggs/minecraft/egg-paper.json b/database/seeds/eggs/minecraft/egg-paper.json deleted file mode 100644 index 60fde38e0..000000000 --- a/database/seeds/eggs/minecraft/egg-paper.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2019-08-01T04:49:37-04:00", - "name": "Paper", - "author": "parker@pterodactyl.io", - "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", - "image": "quay.io\/pterodactyl\/core:java", - "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", - "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", - "startup": "{\r\n \"done\": \")! For help, type \",\r\n \"userInteraction\": [\r\n \"Go to eula.txt for more info.\"\r\n ]\r\n}", - "logs": "{}", - "stop": "stop" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache --update curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft_java\/server.properties\r\nfi", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Minecraft Version", - "description": "The version of minecraft to download. \r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.", - "env_variable": "MINECRAFT_VERSION", - "default_value": "latest", - "user_viewable": 1, - "user_editable": 0, - "rules": "nullable|string|max:20" - }, - { - "name": "Server Jar File", - "description": "The name of the server jarfile to run the server with.", - "env_variable": "SERVER_JARFILE", - "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:20" - }, - { - "name": "Download Path", - "description": "A URL to use to download a server.jar rather than the ones in the install script. This is not user viewable.", - "env_variable": "DL_PATH", - "default_value": "", - "user_viewable": 0, - "user_editable": 0, - "rules": "nullable|string" - }, - { - "name": "Build Number", - "description": "The build number for the paper release.\r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.", - "env_variable": "BUILD_NUMBER", - "default_value": "latest", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|string|max:20" - } - ] -} \ No newline at end of file diff --git a/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json b/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json deleted file mode 100644 index 2bbfba23a..000000000 --- a/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2017-11-03T22:20:03-05:00", - "name": "Sponge (SpongeVanilla)", - "author": "support@pterodactyl.io", - "description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.", - "image": "quay.io\/pterodactyl\/core:java-glibc", - "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", - "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", - "startup": "{\r\n \"done\": \")! For help, type \",\r\n \"userInteraction\": [\r\n \"Go to eula.txt for more info.\"\r\n ]\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "stop" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\n# Sponge Installation Script\n#\n# Server Files: \/mnt\/server\n\napk update\napk add curl\n\ncd \/mnt\/server\n\ncurl -sSL \"https:\/\/repo.spongepowered.org\/maven\/org\/spongepowered\/spongevanilla\/${SPONGE_VERSION}\/spongevanilla-${SPONGE_VERSION}.jar\" -o ${SERVER_JARFILE}", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Sponge Version", - "description": "The version of SpongeVanilla to download and use.", - "env_variable": "SPONGE_VERSION", - "default_value": "1.11.2-6.1.0-BETA-21", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|regex:\/^([a-zA-Z0-9.\\-_]+)$\/" - }, - { - "name": "Server Jar File", - "description": "The name of the Jarfile to use when running SpongeVanilla.", - "env_variable": "SERVER_JARFILE", - "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" - } - ] -} diff --git a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json deleted file mode 100644 index 5937fc176..000000000 --- a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-06-19T17:09:18-04:00", - "name": "Vanilla Minecraft", - "author": "support@pterodactyl.io", - "description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.", - "image": "quay.io\/pterodactyl\/core:java", - "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", - "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", - "startup": "{\r\n \"done\": \")! For help, type \",\r\n \"userInteraction\": [\r\n \"Go to eula.txt for more info.\"\r\n ]\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "stop" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl jq\r\n\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq .versions | jq -r '.[] | select(.id == \"'$LATEST_VERSION'\") | .url')\r\nelse\r\n MANIFEST_URL=$(curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq .versions | jq -r '.[] | select(.id == \"'$VANILLA_VERSION'\") | .url')\r\nfi\r\n\r\nDOWNLOAD_URL=`curl $MANIFEST_URL | jq .downloads.server | jq -r '. | .url'`\r\n\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Server Jar File", - "description": "The name of the server jarfile to run the server with.", - "env_variable": "SERVER_JARFILE", - "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" - }, - { - "name": "Server Version", - "description": "The version of Minecraft Vanilla to install. Use \"latest\" to install the latest version.", - "env_variable": "VANILLA_VERSION", - "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|between:3,15" - } - ] -} diff --git a/database/seeds/eggs/rust/egg-rust.json b/database/seeds/eggs/rust/egg-rust.json deleted file mode 100644 index 7d794cc89..000000000 --- a/database/seeds/eggs/rust/egg-rust.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T16:58:36-06:00", - "name": "Rust", - "author": "support@pterodactyl.io", - "description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.", - "image": "quay.io\/pterodactyl\/core:rust", - "startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} {{ADDITIONAL_ARGS}}", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"Server startup complete\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"latest.log\"\r\n}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "apt update\r\napt -y --no-install-recommends install curl unzip lib32gcc1 ca-certificates\r\ncd \/tmp\r\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\r\n\r\nmkdir -p \/mnt\/server\/steam\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steam\r\ncd \/mnt\/server\/steam\r\nchown -R root:root \/mnt\r\n\r\nexport HOME=\/mnt\/server\r\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 258550 +quit\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v \/mnt\/server\/steam\/linux32\/steamclient.so \/mnt\/server\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Server Name", - "description": "The name of your server in the public server list.", - "env_variable": "HOSTNAME", - "default_value": "A Rust Server", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:40" - }, - { - "name": "OxideMod", - "description": "Set whether you want the server to use and auto update OxideMod or not. Valid options are \"1\" for true and \"0\" for false.", - "env_variable": "OXIDE", - "default_value": "0", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|boolean" - }, - { - "name": "Level", - "description": "The world file for Rust to use.", - "env_variable": "LEVEL", - "default_value": "Procedural Map", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:20" - }, - { - "name": "Description", - "description": "The description under your server title. Commonly used for rules & info. Use \\n for newlines.", - "env_variable": "DESCRIPTION", - "default_value": "Powered by Pterodactyl", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string" - }, - { - "name": "URL", - "description": "The URL for your server. This is what comes up when clicking the \"Visit Website\" button.", - "env_variable": "SERVER_URL", - "default_value": "http:\/\/pterodactyl.io", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|url" - }, - { - "name": "World Size", - "description": "The world size for a procedural map.", - "env_variable": "WORLD_SIZE", - "default_value": "3000", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|integer" - }, - { - "name": "World Seed", - "description": "The seed for a procedural map.", - "env_variable": "WORLD_SEED", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|string" - }, - { - "name": "Max Players", - "description": "The maximum amount of players allowed in the server at once.", - "env_variable": "MAX_PLAYERS", - "default_value": "40", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|integer" - }, - { - "name": "Server Image", - "description": "The header image for the top of your server listing.", - "env_variable": "SERVER_IMG", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|url" - }, - { - "name": "RCON Port", - "description": "Port for RCON connections.", - "env_variable": "RCON_PORT", - "default_value": "28016", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|integer" - }, - { - "name": "RCON Password", - "description": "RCON access password.", - "env_variable": "RCON_PASS", - "default_value": "CHANGEME", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:64" - }, - { - "name": "Save Interval", - "description": "Sets the server’s auto-save interval in seconds.", - "env_variable": "SAVEINTERVAL", - "default_value": "60", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|integer" - }, - { - "name": "Additional Arguments", - "description": "Add additional startup parameters to the server.", - "env_variable": "ADDITIONAL_ARGS", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|string" - } - ] -} diff --git a/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json b/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json deleted file mode 100644 index 35028b20c..000000000 --- a/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2019-02-13T12:30:54-05:00", - "name": "Ark: Survival Evolved", - "author": "support@pterodactyl.io", - "description": "As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! \u2014 Gamepedia: ARK", - "image": "quay.io\/pterodactyl\/core:source", - "startup": ".\/ShooterGame\/Binaries\/Linux\/ShooterGameServer {{SERVER_MAP}}?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"Setting breakpad minidump AppID = 346110\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/bash\n# ARK: Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\n\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\n\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 376030 +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Server Password", - "description": "If specified, players must provide this password to join the server.", - "env_variable": "ARK_PASSWORD", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|alpha_dash|between:1,100" - }, - { - "name": "Admin Password", - "description": "If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.", - "env_variable": "ARK_ADMIN_PASSWORD", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|alpha_dash|between:1,100" - }, - { - "name": "Maximum Players", - "description": "Specifies the maximum number of players that can play on the server simultaneously.", - "env_variable": "SERVER_MAX_PLAYERS", - "default_value": "20", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|numeric|digits_between:1,4" - }, - { - "name": "Server Map", - "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P", - "env_variable": "SERVER_MAP", - "default_value": "TheIsland", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:20" - } - ] -} \ No newline at end of file diff --git a/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json deleted file mode 100644 index ad9210256..000000000 --- a/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-06-19T07:46:06-04:00", - "name": "Counter-Strike: Global Offensive", - "author": "support@pterodactyl.io", - "description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.", - "image": "quay.io\/pterodactyl\/core:source", - "startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"Connection to Steam servers successful\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/bash\n# CSGO Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 740 +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Map", - "description": "The default map for the server.", - "env_variable": "SRCDS_MAP", - "default_value": "de_dust2", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|alpha_dash" - }, - { - "name": "Steam Account Token", - "description": "The Steam Account Token required for the server to be displayed publicly.", - "env_variable": "STEAM_ACC", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|alpha_num|size:32" - }, - { - "name": "Source AppID", - "description": "Required for game to update on server restart. Do not modify this.", - "env_variable": "SRCDS_APPID", - "default_value": "740", - "user_viewable": 0, - "user_editable": 0, - "rules": "required|string|max:20" - } - ] -} diff --git a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json deleted file mode 100644 index 7a7b40bd0..000000000 --- a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T16:59:45-06:00", - "name": "Custom Source Engine Game", - "author": "support@pterodactyl.io", - "description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.", - "image": "quay.io\/pterodactyl\/core:source", - "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/bash\n# SRCDS Base Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Game ID", - "description": "The ID corresponding to the game to download and run using SRCDS.", - "env_variable": "SRCDS_APPID", - "default_value": "", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|numeric|digits_between:1,6" - }, - { - "name": "Game Name", - "description": "The name corresponding to the game to download and run using SRCDS.", - "env_variable": "SRCDS_GAME", - "default_value": "", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|alpha_dash|between:1,100" - }, - { - "name": "Map", - "description": "The default map for the server.", - "env_variable": "SRCDS_MAP", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|alpha_dash" - } - ] -} diff --git a/database/seeds/eggs/source-engine/egg-garrys-mod.json b/database/seeds/eggs/source-engine/egg-garrys-mod.json deleted file mode 100644 index 3b134b0dd..000000000 --- a/database/seeds/eggs/source-engine/egg-garrys-mod.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2019-02-16T14:20:52-05:00", - "name": "Garrys Mod", - "author": "support@pterodactyl.io", - "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", - "image": "quay.io\/pterodactyl\/core:source", - "startup": ".\/srcds_run -game garrysmod -console -port {{SERVER_PORT}} +ip 0.0.0.0 +host_workshop_collection {{WORKSHOP_ID}} +map {{SRCDS_MAP}} +gamemode {{GAMEMODE}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}} +maxplayers {{MAX_PLAYERS}} -tickrate {{TICKRATE}}", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/bash\r\n# Garry's Mod Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\ncd \/tmp\r\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\r\n\r\nmkdir -p \/mnt\/server\/steamcmd\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\n\r\nexport HOME=\/mnt\/server\r\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 4020 +quit\r\n\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n# Creating needed default files for the game\r\ncd \/mnt\/server\/garrysmod\/lua\/autorun\/server\r\necho '\r\n-- Docs: https:\/\/wiki.garrysmod.com\/page\/resource\/AddWorkshop\r\n-- Place the ID of the workshop addon you want to be downloaded to people who join your server, not the collection ID\r\n-- Use https:\/\/beta.configcreator.com\/create\/gmod\/resources.lua to easily create a list based on your collection ID\r\n\r\nresource.AddWorkshop( \"\" )\r\n' > workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Map", - "description": "The default map for the server.", - "env_variable": "SRCDS_MAP", - "default_value": "gm_flatgrass", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|alpha_dash" - }, - { - "name": "Steam Account Token", - "description": "The Steam Account Token required for the server to be displayed publicly.", - "env_variable": "STEAM_ACC", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|string|alpha_num|size:32" - }, - { - "name": "Source AppID", - "description": "Required for game to update on server restart. Do not modify this.", - "env_variable": "SRCDS_APPID", - "default_value": "4020", - "user_viewable": 0, - "user_editable": 0, - "rules": "required|string|max:20" - }, - { - "name": "Workshop ID", - "description": "The ID of your workshop collection (the numbers at the end of the URL)", - "env_variable": "WORKSHOP_ID", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|integer" - }, - { - "name": "Gamemode", - "description": "The gamemode of your server.", - "env_variable": "GAMEMODE", - "default_value": "sandbox", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string" - }, - { - "name": "Max Players", - "description": "The maximum amount of players allowed on your game server.", - "env_variable": "MAX_PLAYERS", - "default_value": "32", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|integer|max:128" - }, - { - "name": "Tickrate", - "description": "The tickrate defines how fast the server will update each entities location.", - "env_variable": "TICKRATE", - "default_value": "22", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|integer|max:100" - } - ] -} diff --git a/database/seeds/eggs/source-engine/egg-insurgency.json b/database/seeds/eggs/source-engine/egg-insurgency.json deleted file mode 100644 index 950743fdd..000000000 --- a/database/seeds/eggs/source-engine/egg-insurgency.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T16:59:48-06:00", - "name": "Insurgency", - "author": "support@pterodactyl.io", - "description": "Take to the streets for intense close quarters combat, where a team's survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.", - "image": "quay.io\/pterodactyl\/core:source", - "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", - "config": { - "files": "{}", - "startup": "{\"done\": \"gameserver Steam ID\", \"userInteraction\": []}", - "logs": "{\"custom\": true, \"location\": \"logs\/latest.log\"}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/bash\n# SRCDS Base Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Game ID", - "description": "The ID corresponding to the game to download and run using SRCDS.", - "env_variable": "SRCDS_APPID", - "default_value": "237410", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|regex:\/^(237410)$\/" - }, - { - "name": "Game Name", - "description": "The name corresponding to the game to download and run using SRCDS.", - "env_variable": "SRCDS_GAME", - "default_value": "insurgency", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|regex:\/^(insurgency)$\/" - }, - { - "name": "Default Map", - "description": "The default map to use when starting the server.", - "env_variable": "SRCDS_MAP", - "default_value": "sinjar", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^(\\w{1,20})$\/" - } - ] -} diff --git a/database/seeds/eggs/source-engine/egg-team-fortress2.json b/database/seeds/eggs/source-engine/egg-team-fortress2.json deleted file mode 100644 index ae443370c..000000000 --- a/database/seeds/eggs/source-engine/egg-team-fortress2.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T16:59:45-06:00", - "name": "Team Fortress 2", - "author": "support@pterodactyl.io", - "description": "Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.", - "image": "quay.io\/pterodactyl\/core:source", - "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", - "stop": "quit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/bash\n# SRCDS Base Installation Script\n#\n# Server Files: \/mnt\/server\napt -y update\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\n\ncd \/tmp\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\n\nmkdir -p \/mnt\/server\/steamcmd\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\ncd \/mnt\/server\/steamcmd\n\n# SteamCMD fails otherwise for some reason, even running as root.\n# This is changed at the end of the install process anyways.\nchown -R root:root \/mnt\n\nexport HOME=\/mnt\/server\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} +quit\n\nmkdir -p \/mnt\/server\/.steam\/sdk32\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", - "entrypoint": "bash" - } - }, - "variables": [ - { - "name": "Game ID", - "description": "The ID corresponding to the game to download and run using SRCDS.", - "env_variable": "SRCDS_APPID", - "default_value": "232250", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|regex:\/^(232250)$\/" - }, - { - "name": "Game Name", - "description": "The name corresponding to the game to download and run using SRCDS.", - "env_variable": "SRCDS_GAME", - "default_value": "tf", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|regex:\/^(tf)$\/" - }, - { - "name": "Default Map", - "description": "The default map to use when starting the server.", - "env_variable": "SRCDS_MAP", - "default_value": "cp_dustbowl", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^(\\w{1,20})$\/" - } - ] -} \ No newline at end of file diff --git a/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json b/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json deleted file mode 100644 index 9381890d1..000000000 --- a/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T17:01:31-06:00", - "name": "Terraria Server (TShock)", - "author": "support@pterodactyl.io", - "description": "TShock is a server modification for Terraria, written in C#, and based upon the Terraria Server API. It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.", - "image": "quay.io\/pterodactyl\/core:mono", - "startup": null, - "config": { - "files": "{\"tshock\/config.json\":{\"parser\": \"json\", \"find\":{\"ServerPort\": \"{{server.build.default.port}}\", \"MaxSlots\": \"{{server.build.env.MAX_SLOTS}}\"}}}", - "startup": "{\"done\": \"Type 'help' for a list of commands\", \"userInteraction\": []}", - "logs": "{\"custom\": false, \"location\": \"ServerLog.txt\"}", - "stop": "exit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\n# TShock Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl unzip\n\ncd \/tmp\n\ncurl -sSLO https:\/\/github.com\/NyxStudios\/TShock\/releases\/download\/v${T_VERSION}\/tshock_${T_VERSION}.zip\n\nunzip -o tshock_${T_VERSION}.zip -d \/mnt\/server", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "TShock Version", - "description": "Which version of TShock to install and use.", - "env_variable": "T_VERSION", - "default_value": "4.3.22", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([0-9_\\.-]{5,10})$\/" - }, - { - "name": "Maximum Slots", - "description": "Total number of slots to allow on the server.", - "env_variable": "MAX_SLOTS", - "default_value": "20", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|numeric|digits_between:1,3" - } - ] -} diff --git a/database/seeds/eggs/voice-servers/egg-mumble-server.json b/database/seeds/eggs/voice-servers/egg-mumble-server.json deleted file mode 100644 index e62562597..000000000 --- a/database/seeds/eggs/voice-servers/egg-mumble-server.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T17:01:44-06:00", - "name": "Mumble Server", - "author": "support@pterodactyl.io", - "description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.", - "image": "quay.io\/pterodactyl\/core:glibc", - "startup": ".\/murmur.x86 -fg", - "config": { - "files": "{\"murmur.ini\":{\"parser\": \"ini\", \"find\":{\"logfile\": \"murmur.log\", \"port\": \"{{server.build.default.port}}\", \"host\": \"0.0.0.0\", \"users\": \"{{server.build.env.MAX_USERS}}\"}}}", - "startup": "{\"done\": \"Server listening on\", \"userInteraction\": [ \"Generating new server certificate\"]}", - "logs": "{\"custom\": true, \"location\": \"logs\/murmur.log\"}", - "stop": "^C" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\n# Mumble Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add tar curl\n\ncd \/tmp\n\ncurl -sSLO https:\/\/github.com\/mumble-voip\/mumble\/releases\/download\/${MUMBLE_VERSION}\/murmur-static_x86-${MUMBLE_VERSION}.tar.bz2\n\ntar -xjvf murmur-static_x86-${MUMBLE_VERSION}.tar.bz2\ncp -r murmur-static_x86-${MUMBLE_VERSION}\/* \/mnt\/server", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Maximum Users", - "description": "Maximum concurrent users on the mumble server.", - "env_variable": "MAX_USERS", - "default_value": "100", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|numeric|digits_between:1,5" - }, - { - "name": "Server Version", - "description": "Version of Mumble Server to download and use.", - "env_variable": "MUMBLE_VERSION", - "default_value": "1.2.19", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([0-9_\\.-]{5,8})$\/" - } - ] -} diff --git a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json deleted file mode 100644 index d3d889bc0..000000000 --- a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2019-07-05T11:59:29-04:00", - "name": "Teamspeak3 Server", - "author": "support@pterodactyl.io", - "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", - "image": "quay.io/parkervcp/pterodactyl-images:base_debian", - "startup": "./ts3server default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1", - "config": { - "files": "{}", - "startup": "{\r\n \"done\": \"listening on 0.0.0.0:\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs/ts3.log\"\r\n}", - "stop": "^C" - }, - "scripts": { - "installation": { - "script": "#!/bin/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: /mnt/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https://teamspeak.com/versions/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd /mnt/server\r\n\r\n\r\necho -e \"getting files from http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "Server Version", - "description": "The version of Teamspeak 3 to use when running the server.", - "env_variable": "TS_VERSION", - "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:6" - }, - { - "name": "File Transfer Port", - "description": "The Teamspeak file transfer port", - "env_variable": "FILE_TRANSFER", - "default_value": "30033", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|integer|between:1,65535" - } - ] -} diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml new file mode 100644 index 000000000..35b4f1530 --- /dev/null +++ b/docker-compose.development.yaml @@ -0,0 +1,125 @@ +# For more information: https://laravel.com/docs/sail +version: '3' + +services: + caddy: + image: localhost/pterodactyl/development:panel + network_mode: host + command: + - caddy + - run + - --config + - /etc/caddy/Caddyfile + volumes: + - '.:/var/www/html' + depends_on: + - laravel + + laravel: + image: localhost/pterodactyl/development:panel + network_mode: host + command: + - 'php-fpm' + - '--nodaemonize' + - '-y' + - '/etc/php-fpm.conf' + volumes: + - '.:/var/www/html' + tmpfs: + - '/tmp' + depends_on: + - pgsql + - mariadb + - redis + - mailhog + + pgsql: + image: docker.io/library/postgres:14 + ports: + - '127.0.0.1:${FORWARD_DB_PORT:-5432}:5432' + environment: + PGPASSWORD: '${DB_PASSWORD:-secret}' + POSTGRES_DB: '${DB_DATABASE}' + POSTGRES_USER: '${DB_USERNAME}' + POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}' + volumes: + - 'sail-pgsql:/var/lib/postgresql/data' + - './vendor/laravel/sail/database/pgsql/create-testing-database.sql:/docker-entrypoint-initdb.d/10-create-testing-database.sql' + networks: + - sail + healthcheck: + test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE}", "-U", "${DB_USERNAME}"] + retries: 3 + timeout: 5s + + mariadb: + image: docker.io/library/mariadb:10 + ports: + - '127.0.0.1:${FORWARD_DB_PORT:-3306}:3306' + environment: + MYSQL_DATABASE: '${DB_DATABASE}' + MYSQL_USER: '${DB_USERNAME}' + MYSQL_PASSWORD: '${DB_PASSWORD}' + MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' + volumes: + - 'sail-mariadb:/var/lib/mysql' + - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' + networks: + - sail + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] + retries: 3 + timeout: 5s + + redis: + image: docker.io/library/redis:7 + ports: + - '127.0.0.1:${FORWARD_REDIS_PORT:-6379}:6379' + volumes: + - 'sail-redis:/data' + networks: + - sail + healthcheck: + test: ["CMD", "redis-cli", "ping"] + retries: 3 + timeout: 5s + + minio: + image: docker.io/minio/minio:latest + ports: + - '127.0.0.1:${FORWARD_MINIO_PORT:-9001}:9000' + - '127.0.0.1:${FORWARD_MINIO_CONSOLE_PORT:-8900}:8900' + environment: + MINIO_ROOT_USER: 'sail' + MINIO_ROOT_PASSWORD: 'password' + volumes: + - 'sail-minio:/data/minio' + networks: + - sail + command: minio server /data/minio --console-address ":8900" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + retries: 3 + timeout: 5s + + mailhog: + image: docker.io/mailhog/mailhog:latest + ports: + - '127.0.0.1:${FORWARD_MAILHOG_PORT:-1025}:1025' + - '127.0.0.1:${FORWARD_MAILHOG_DASHBOARD_PORT:-8025}:8025' + networks: + - sail + +networks: + sail: + driver: bridge + +volumes: + sail-pgsql: + driver: local + sail-mariadb: + driver: local + sail-redis: + driver: local + sail-minio: + driver: local diff --git a/docker-compose.example.yml b/docker-compose.example.yml index eb1bfaddf..a0fbaba13 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -1,22 +1,54 @@ -version: '2' +version: '3.8' +x-common: + database: + &db-environment + # Do not remove the "&db-password" from the end of the line below, it is important + # for Panel functionality. + MYSQL_PASSWORD: &db-password "CHANGE_ME" + MYSQL_ROOT_PASSWORD: "CHANGE_ME_TOO" + panel: + &panel-environment + APP_URL: "http://example.com" + # A list of valid timezones can be found here: http://php.net/manual/en/timezones.php + APP_TIMEZONE: "UTC" + APP_SERVICE_AUTHOR: "noreply@example.com" + # Uncomment the line below and set to a non-empty value if you want to use Let's Encrypt + # to generate an SSL certificate for the Panel. + # LE_EMAIL: "" + mail: + &mail-environment + MAIL_FROM: "noreply@example.com" + MAIL_DRIVER: "smtp" + MAIL_HOST: "mail" + MAIL_PORT: "1025" + MAIL_USERNAME: "" + MAIL_PASSWORD: "" + MAIL_ENCRYPTION: "true" + +# +# ------------------------------------------------------------------------------------------ +# DANGER ZONE BELOW +# +# The remainder of this file likely does not need to be changed. Please only make modifications +# below if you understand what you are doing. +# services: database: - image: mariadb + image: mariadb:10.5 + restart: always + command: --default-authentication-plugin=mysql_native_password volumes: - "/srv/pterodactyl/database:/var/lib/mysql" environment: - ## Database settings - ## change if you want it to be more secure. - - "MYSQL_ROOT_PASSWORD=apassword" - - "MYSQL_DATABASE=pterodb" - - "MYSQL_USER=ptero" - - "MYSQL_PASSWORD=pterodbpass" - + <<: *db-environment + MYSQL_DATABASE: "panel" + MYSQL_USER: "pterodactyl" cache: image: redis:alpine - + restart: always panel: - image: quay.io/pterodactyl/panel:latest + image: ghcr.io/pterodactyl/panel:latest + restart: always ports: - "80:80" - "443:443" @@ -25,51 +57,20 @@ services: - cache volumes: - "/srv/pterodactyl/var/:/app/var/" - - "/srv/pterodactyl/nginx/:/etc/nginx/conf.d/" + - "/srv/pterodactyl/nginx/:/etc/nginx/http.d/" - "/srv/pterodactyl/certs/:/etc/letsencrypt/" - - "/srv/pterodactyl/logs/:/var/log/" + - "/srv/pterodactyl/logs/:/app/storage/logs" environment: - ## These are defaults and should be left alone - - "APP_ENV=production" - - "APP_DEBUG=false" - - "APP_THEME=pterodactyl" - - "APP_CLEAR_TASKLOG=720" - - "APP_DELETE_MINUTES=10" - - "APP_ENVIRONMENT_ONLY=false" - - "QUEUE_HIGH=high" - - "QUEUE_STANDARD=standard" - - "QUEUE_LOW=low" - ## Cache settings - - "CACHE_DRIVER=redis" - - "SESSION_DRIVER=redis" - - "QUEUE_DRIVER=redis" - - "REDIS_HOST=cache" - - "REDIS_PASSWORD=null" - - "REDIS_PORT=6379" - ## Domain settings - - "APP_URL=https://your.domain.here" ## if you are running this behind a reverse proxy with ssl app_url needs to be https still. - ## Timezone settings - - "APP_TIMEZONE=UTC" ## http://php.net/manual/en/timezones.php - ## Service egg settings - - "APP_SERVICE_AUTHOR=noreply@your.domain.here" ## this is the email that gets put on eggs you create - ## Database settings - ## These can be left alone. Only change if you know what you are doing. - - "DB_HOST=database" - - "DB_PORT=3306" - - "DB_DATABASE=pterodb" - - "DB_USERNAME=ptero" - - "DB_PASSWORD=pterodbpass" - ## Email settings - - "MAIL_FROM=noreply@your.domain.here" - - "MAIL_DRIVER=smtp" - - "MAIL_HOST=mail" - - "MAIL_PORT=1025" - - "MAIL_USERNAME=''" - - "MAIL_PASSWORD=''" - - "MAIL_ENCRYPTION=true" - ## certbot settings - Used to automatically generate ssl certs and - - "LE_EMAIL=''" ## leave blank unless you aree generating certs. - + <<: [*panel-environment, *mail-environment] + DB_PASSWORD: *db-password + APP_ENV: "production" + APP_ENVIRONMENT_ONLY: "false" + CACHE_DRIVER: "redis" + SESSION_DRIVER: "redis" + QUEUE_DRIVER: "redis" + REDIS_HOST: "cache" + DB_HOST: "database" + DB_PORT: "3306" networks: default: ipam: diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..f0b2fa2a7 --- /dev/null +++ b/flake.lock @@ -0,0 +1,326 @@ +{ + "nodes": { + "devshell": { + "flake": false, + "locked": { + "lastModified": 1663445644, + "narHash": "sha256-+xVlcK60x7VY1vRJbNUEAHi17ZuoQxAIH4S4iUFUGBA=", + "owner": "numtide", + "repo": "devshell", + "rev": "e3dc3e21594fe07bdb24bdf1c8657acaa4cb8f66", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "dream2nix": { + "inputs": { + "devshell": "devshell", + "flake-compat": "flake-compat", + "flake-parts": "flake-parts", + "nix-unit": "nix-unit", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1695717405, + "narHash": "sha256-MvHrU3h0Bw57s2p+wCUnSZliR4wvvPi3xkW+MRWB5HU=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "6dbd59e4a47bd916a655c4425a3e730c6aeae033", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "dream2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1675933616, + "narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "47478a4a003e745402acf63be7f9a092d51b83d7", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "mk-node-package": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "npmlock2nix": "npmlock2nix", + "pnpm2nix": "pnpm2nix" + }, + "locked": { + "lastModified": 1633790997, + "narHash": "sha256-1mk4EwNkWtTNpeRivZmJTzB+92g07maeFRVUMnnRh1U=", + "owner": "winston0410", + "repo": "mkNodePackage", + "rev": "a7eca5e027c8b260dca4ece7d8dd187f92420611", + "type": "github" + }, + "original": { + "owner": "winston0410", + "repo": "mkNodePackage", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "nix-unit", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix-unit": { + "inputs": { + "flake-parts": [ + "dream2nix", + "flake-parts" + ], + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "dream2nix", + "nixpkgs" + ], + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1690289081, + "narHash": "sha256-PCXQAQt8+i2pkUym9P1JY4JGoeZJLzzxWBhprHDdItM=", + "owner": "adisbladis", + "repo": "nix-unit", + "rev": "a9d6f33e50d4dcd9cfc0c92253340437bbae282b", + "type": "github" + }, + "original": { + "owner": "adisbladis", + "repo": "nix-unit", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1695644571, + "narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "npmlock2nix": { + "flake": false, + "locked": { + "lastModified": 1633729941, + "narHash": "sha256-v2YPcEWI1Wz8ErivorubgLcDT06H6YzFT7uhp1ymqnE=", + "owner": "winston0410", + "repo": "npmlock2nix", + "rev": "6ade47a330b6919defb45c0eb984a64234aa8468", + "type": "github" + }, + "original": { + "owner": "winston0410", + "ref": "issue113", + "repo": "npmlock2nix", + "type": "github" + } + }, + "pnpm2nix": { + "flake": false, + "locked": { + "lastModified": 1594396611, + "narHash": "sha256-UXOUQ+2A89/zaxYhTHiRrRBU5exbUWrg+FoJYMcNwuI=", + "owner": "nix-community", + "repo": "pnpm2nix", + "rev": "f67be0925a91b92f54d99dbdead7a06920b979ac", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "pnpm2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "dream2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1646153636, + "narHash": "sha256-AlWHMzK+xJ1mG267FdT8dCq/HvLCA6jwmx2ZUy5O8tY=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "b6bc0b21e1617e2b07d8205e7fae7224036dfa4b", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "dream2nix": "dream2nix", + "flake-utils": "flake-utils_2", + "mk-node-package": "mk-node-package", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "nix-unit", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1689620039, + "narHash": "sha256-BtNwghr05z7k5YMdq+6nbue+nEalvDepuA7qdQMAKoQ=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "719c2977f958c41fa60a928e2fbc50af14844114", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..4ae6586aa --- /dev/null +++ b/flake.nix @@ -0,0 +1,260 @@ +{ + description = "Pterodactyl Panel"; + + inputs = { + dream2nix = { + url = "github:nix-community/dream2nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-utils = { + url = "github:numtide/flake-utils"; + }; + + mk-node-package = { + url = "github:winston0410/mkNodePackage"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + nixpkgs = { + url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + }; + + outputs = { + self, + dream2nix, + flake-utils, + mk-node-package, + nixpkgs, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let + version = "latest"; + + pkgs = import nixpkgs {inherit system;}; + mkNodePackage = mk-node-package.lib."${system}".mkNodePackage; + + php81WithExtensions = with pkgs; (php81.buildEnv { + extensions = { + enabled, + all, + }: + enabled + ++ (with all; [ + redis + xdebug + ]); + extraConfig = '' + xdebug.mode=debug + ''; + }); + composer = with pkgs; (php81Packages.composer.override {php = php81WithExtensions;}); + + caCertificates = pkgs.runCommand "ca-certificates" {} '' + mkdir -p $out/etc/ssl/certs $out/etc/pki/tls/certs + ln -s ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs/ca-bundle.crt + ln -s ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs/ca-certificates.crt + ln -s ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/pki/tls/certs/ca-bundle.crt + ''; + + caddyfile = pkgs.writeText "Caddyfile" '' + :80 { + root * /var/www/html/public/ + file_server + + header { + -Server + -X-Powered-By + Referrer-Policy "same-origin" + X-Frame-Options "deny" + X-XSS-Protection "1; mode=block" + X-Content-Type-Options "nosniff" + } + + encode gzip zstd + + php_fastcgi localhost:9000 + + try_files {path} {path}/ /index.php?{query} + } + ''; + + phpfpmConf = pkgs.writeText "php-fpm.conf" '' + [global] + error_log = /dev/stderr + daemonize = no + + [www] + user = nobody + group = nobody + + listen = 0.0.0.0:9000 + + pm = dynamic + pm.start_servers = 4 + pm.min_spare_servers = 4 + pm.max_spare_servers = 16 + pm.max_children = 64 + pm.max_requests = 256 + + clear_env = no + catch_workers_output = yes + + decorate_workers_output = no + ''; + + configs = pkgs.runCommand "configs" {} '' + mkdir -p $out/etc/caddy + ln -s ${caddyfile} $out/etc/caddy/Caddyfile + ln -s ${phpfpmConf} $out/etc/php-fpm.conf + ''; + + src = with pkgs.lib; + cleanSource (cleanSourceWith { + filter = name: type: let + baseName = baseNameOf (toString name); + in + !(builtins.elem baseName [ + ".direnv" + ".github" + "bootstrap/cache" + "node_modules" + "public/build" + "public/hot" + "storage" + "vendor" + ".editorconfig" + ".env" + ".env.testing" + ".envrc" + ".gitignore" + ".php-cs-fixer.cache" + ".phpunit.result.cache" + "BUILDING.md" + "CODE_OF_CONDUCT.md" + "CONTRIBUTING.md" + "docker-compose.development.yaml" + "docker-compose.example.yaml" + "docker-compose.yaml" + "flake.lock" + "flake.nix" + "shell.nix" + ]); + src = ./.; + }); + + app = + (dream2nix.lib.makeFlakeOutputs { + config.projectRoot = src; + source = src; + settings = [ + { + translator = "composer-lock"; + subsystemInfo.noDev = true; + } + ]; + systems = [system]; + autoProjects = true; + }) + .packages + ."${system}" + ."pterodactyl/panel"; + + ui = mkNodePackage { + inherit src version; + + pname = "pterodactyl"; + buildInputs = []; + + buildPhase = '' + pnpm run build + ''; + + installPhase = '' + mkdir -p $out + cp -r public/build $out + ''; + }; + + panel = pkgs.stdenv.mkDerivation { + inherit src version; + + pname = "pterodactyl"; + buildInputs = [app ui]; + + installPhase = '' + cp -r ${app}/lib/vendor/pterodactyl/panel $out + + chmod 755 $out + chmod 755 $out/public + + mkdir -p $out/public/build + cp -r ${ui}/build/* $out/public/build + + rm $out/composer.json.orig + ''; + }; + in { + defaultPackage = panel; + devShell = import ./shell.nix {inherit composer php81WithExtensions pkgs;}; + + packages = { + inherit panel; + + development = with pkgs; + dockerTools.buildImage { + name = "pterodactyl/development"; + tag = "panel"; + + copyToRoot = pkgs.buildEnv { + name = "image-root"; + paths = [ + bash + dockerTools.fakeNss + caCertificates + caddy + composer + configs + coreutils + mysql80 + nodejs-18_x + nodePackages.pnpm + php81WithExtensions + postgresql_15 + ]; + pathsToLink = ["/bin" "/etc"]; + }; + }; + + oci = with pkgs; + dockerTools.buildImage { + name = "pterodactyl/panel"; + tag = version; + + copyToRoot = buildEnv { + name = "image-root"; + paths = [ + dockerTools.fakeNss + caCertificates + caddy + configs + php81WithExtensions + + panel + ]; + pathsToLink = ["/bin" "/etc"]; + }; + + config = { + Cmd = []; + }; + }; + }; + } + ); +} diff --git a/package.json b/package.json index 2a9697409..ec5008160 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,148 @@ { - "name": "pterodactyl-panel", - "devDependencies": { - "babel-cli": "6.18.0", - "babel-plugin-transform-strict-mode": "^6.18.0", - "babel-preset-es2015": "6.18.0" - }, - "scripts": { - "build": "./node_modules/babel-cli/bin/babel.js public/themes/pterodactyl/js/frontend/files/src --source-maps --out-file public/themes/pterodactyl/js/frontend/files/filemanager.min.js" - } + "name": "@pterodactyl/panel", + "version": "1.0.0", + "license": "MIT", + "private": true, + "packageManager": "pnpm@8.7.6", + "engines": { + "node": ">=16.13" + }, + "scripts": { + "build": "vite build", + "clean": "rimraf public/build", + "coverage": "vitest run --coverage", + "dev": "vite", + "lint": "eslint ./resources/scripts/**/*.{ts,tsx} --ext .ts,.tsx", + "test": "vitest run", + "test:ui": "vitest --ui" + }, + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/lang-cpp": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-java": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/lang-json": "^6.0.0", + "@codemirror/lang-lezer": "^6.0.0", + "@codemirror/lang-markdown": "^6.0.0", + "@codemirror/lang-php": "^6.0.0", + "@codemirror/lang-python": "^6.0.0", + "@codemirror/lang-rust": "^6.0.0", + "@codemirror/lang-sql": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/language-data": "^6.0.0", + "@codemirror/legacy-modes": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@floating-ui/react-dom-interactions": "0.13.3", + "@flyyer/use-fit-text": "3.0.1", + "@fortawesome/fontawesome-svg-core": "6.3.0", + "@fortawesome/free-brands-svg-icons": "6.3.0", + "@fortawesome/free-solid-svg-icons": "6.3.0", + "@fortawesome/react-fontawesome": "0.2.0", + "@headlessui/react": "1.7.11", + "@heroicons/react": "1.0.6", + "@lezer/common": "1.0.2", + "@lezer/highlight": "1.1.3", + "@preact/signals-react": "1.2.2", + "axios": "0.27.2", + "boring-avatars": "1.7.0", + "chart.js": "3.9.1", + "classnames": "2.3.2", + "copy-to-clipboard": "3.3.3", + "date-fns": "2.29.3", + "debounce": "1.2.1", + "deepmerge-ts": "4.3.0", + "easy-peasy": "5.2.0", + "events": "3.3.0", + "formik": "2.2.9", + "framer-motion": "9.1.6", + "i18next": "22.4.10", + "i18next-http-backend": "2.1.1", + "i18next-multiload-backend-adapter": "2.2.0", + "nanoid": "4.0.1", + "qrcode.react": "3.1.0", + "react": "18.2.0", + "react-chartjs-2": "4.3.1", + "react-dom": "18.2.0", + "react-fast-compare": "3.2.0", + "react-i18next": "12.2.0", + "react-router-dom": "6.8.1", + "react-select": "5.7.0", + "reaptcha": "1.12.1", + "sockette": "2.0.6", + "styled-components": "5.3.6", + "styled-components-breakpoint": "3.0.0-preview.20", + "swr": "2.0.3", + "xterm": "5.1.0", + "xterm-addon-fit": "0.7.0", + "xterm-addon-search": "0.11.0", + "xterm-addon-search-bar": "0.2.0", + "xterm-addon-web-links": "0.8.0", + "yup": "1.0.0" + }, + "devDependencies": { + "@tailwindcss/forms": "0.5.3", + "@tailwindcss/line-clamp": "0.4.2", + "@testing-library/dom": "9.0.0", + "@testing-library/react": "14.0.0", + "@testing-library/user-event": "14.4.3", + "@types/debounce": "1.2.1", + "@types/events": "3.0.0", + "@types/node": "18.14.1", + "@types/react": "18.0.28", + "@types/react-dom": "18.0.11", + "@types/styled-components": "5.1.26", + "@typescript-eslint/eslint-plugin": "5.53.0", + "@typescript-eslint/parser": "5.53.0", + "@vitejs/plugin-react": "3.1.0", + "autoprefixer": "10.4.13", + "babel-plugin-styled-components": "2.0.7", + "babel-plugin-twin": "1.1.0", + "cross-env": "7.0.3", + "eslint": "8.34.0", + "eslint-config-prettier": "8.6.0", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-prettier": "4.2.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", + "happy-dom": "8.7.2", + "laravel-vite-plugin": "0.7.4", + "pathe": "1.1.0", + "postcss": "8.4.21", + "postcss-import": "15.1.0", + "postcss-nesting": "11.2.1", + "postcss-preset-env": "8.0.1", + "prettier": "2.8.4", + "prettier-plugin-tailwindcss": "0.2.3", + "rimraf": "3.0.2", + "tailwindcss": "3.2.7", + "ts-essentials": "9.3.0", + "twin.macro": "2.8.2", + "typescript": "4.9.5", + "vite": "4.1.4", + "vitest": "0.28.5" + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "firefox esr", + "not dead" + ], + "babelMacros": { + "twin": { + "config": "tailwind.config.js", + "preset": "styled-components" + }, + "styledComponents": { + "pure": true, + "displayName": true, + "fileName": true + } + } } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..9d13c6c6c --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,26 @@ +includes: + - ./vendor/nunomaduro/larastan/extension.neon + +parameters: + + paths: + - app/ + + # Level 9 is the highest level + level: 4 + + ignoreErrors: + # Ignore repository interface missing methods + - '#Call to an undefined method Pterodactyl\\Repositories\\Wings\\DaemonRepository::(\w+)\(\)#' + + # Ignore magic spatie calls + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::allowed(\w+)\(\)#' + + excludePaths: + - app/Repositories + + # More magic spatie to be replaced + - app/Extensions/Spatie/Fractalistic/Fractal.php + +# +# checkMissingIterableValueType: false diff --git a/phpunit.xml b/phpunit.xml index c0a7cf837..531bab69a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,34 +1,30 @@ - + + + + ./app + + - ./tests/Integration + ./tests/Integration - ./tests/Unit + ./tests/Unit - - - ./app - - - - - - - - - + + + + + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..bf8c5dbc4 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5803 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@codemirror/autocomplete': + specifier: ^6.0.0 + version: 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/commands': + specifier: ^6.0.0 + version: 6.2.1 + '@codemirror/lang-cpp': + specifier: ^6.0.0 + version: 6.0.2 + '@codemirror/lang-css': + specifier: ^6.0.0 + version: 6.0.2(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-html': + specifier: ^6.0.0 + version: 6.4.2 + '@codemirror/lang-java': + specifier: ^6.0.0 + version: 6.0.1 + '@codemirror/lang-javascript': + specifier: ^6.0.0 + version: 6.1.4 + '@codemirror/lang-json': + specifier: ^6.0.0 + version: 6.0.1 + '@codemirror/lang-lezer': + specifier: ^6.0.0 + version: 6.0.1 + '@codemirror/lang-markdown': + specifier: ^6.0.0 + version: 6.1.0 + '@codemirror/lang-php': + specifier: ^6.0.0 + version: 6.0.1 + '@codemirror/lang-python': + specifier: ^6.0.0 + version: 6.1.1(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-rust': + specifier: ^6.0.0 + version: 6.0.1 + '@codemirror/lang-sql': + specifier: ^6.0.0 + version: 6.4.0(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-xml': + specifier: ^6.0.0 + version: 6.0.2(@codemirror/view@6.9.1) + '@codemirror/language': + specifier: ^6.0.0 + version: 6.6.0 + '@codemirror/language-data': + specifier: ^6.0.0 + version: 6.1.0(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/legacy-modes': + specifier: ^6.0.0 + version: 6.3.1 + '@codemirror/lint': + specifier: ^6.0.0 + version: 6.1.1 + '@codemirror/search': + specifier: ^6.0.0 + version: 6.2.3 + '@codemirror/state': + specifier: ^6.0.0 + version: 6.2.0 + '@codemirror/view': + specifier: ^6.0.0 + version: 6.9.1 + '@floating-ui/react-dom-interactions': + specifier: 0.13.3 + version: 0.13.3(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@flyyer/use-fit-text': + specifier: 3.0.1 + version: 3.0.1(react@18.2.0) + '@fortawesome/fontawesome-svg-core': + specifier: 6.3.0 + version: 6.3.0 + '@fortawesome/free-brands-svg-icons': + specifier: 6.3.0 + version: 6.3.0 + '@fortawesome/free-solid-svg-icons': + specifier: 6.3.0 + version: 6.3.0 + '@fortawesome/react-fontawesome': + specifier: 0.2.0 + version: 0.2.0(@fortawesome/fontawesome-svg-core@6.3.0)(react@18.2.0) + '@headlessui/react': + specifier: 1.7.11 + version: 1.7.11(react-dom@18.2.0)(react@18.2.0) + '@heroicons/react': + specifier: 1.0.6 + version: 1.0.6(react@18.2.0) + '@lezer/common': + specifier: 1.0.2 + version: 1.0.2 + '@lezer/highlight': + specifier: 1.1.3 + version: 1.1.3 + '@preact/signals-react': + specifier: 1.2.2 + version: 1.2.2(react@18.2.0) + axios: + specifier: 0.27.2 + version: 0.27.2 + boring-avatars: + specifier: 1.7.0 + version: 1.7.0 + chart.js: + specifier: 3.9.1 + version: 3.9.1 + classnames: + specifier: 2.3.2 + version: 2.3.2 + copy-to-clipboard: + specifier: 3.3.3 + version: 3.3.3 + date-fns: + specifier: 2.29.3 + version: 2.29.3 + debounce: + specifier: 1.2.1 + version: 1.2.1 + deepmerge-ts: + specifier: 4.3.0 + version: 4.3.0 + easy-peasy: + specifier: 5.2.0 + version: 5.2.0(@types/react-dom@18.0.11)(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + events: + specifier: 3.3.0 + version: 3.3.0 + formik: + specifier: 2.2.9 + version: 2.2.9(react@18.2.0) + framer-motion: + specifier: 9.1.6 + version: 9.1.6(react-dom@18.2.0)(react@18.2.0) + i18next: + specifier: 22.4.10 + version: 22.4.10 + i18next-http-backend: + specifier: 2.1.1 + version: 2.1.1 + i18next-multiload-backend-adapter: + specifier: 2.2.0 + version: 2.2.0 + nanoid: + specifier: 4.0.1 + version: 4.0.1 + qrcode.react: + specifier: 3.1.0 + version: 3.1.0(react@18.2.0) + react: + specifier: 18.2.0 + version: 18.2.0 + react-chartjs-2: + specifier: 4.3.1 + version: 4.3.1(chart.js@3.9.1)(react@18.2.0) + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-fast-compare: + specifier: 3.2.0 + version: 3.2.0 + react-i18next: + specifier: 12.2.0 + version: 12.2.0(i18next@22.4.10)(react-dom@18.2.0)(react@18.2.0) + react-router-dom: + specifier: 6.8.1 + version: 6.8.1(react-dom@18.2.0)(react@18.2.0) + react-select: + specifier: 5.7.0 + version: 5.7.0(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + reaptcha: + specifier: 1.12.1 + version: 1.12.1(react@18.2.0) + sockette: + specifier: 2.0.6 + version: 2.0.6 + styled-components: + specifier: 5.3.6 + version: 5.3.6(react-dom@18.2.0)(react-is@17.0.2)(react@18.2.0) + styled-components-breakpoint: + specifier: 3.0.0-preview.20 + version: 3.0.0-preview.20(@types/styled-components@5.1.26)(styled-components@5.3.6) + swr: + specifier: 2.0.3 + version: 2.0.3(react@18.2.0) + xterm: + specifier: 5.1.0 + version: 5.1.0 + xterm-addon-fit: + specifier: 0.7.0 + version: 0.7.0(xterm@5.1.0) + xterm-addon-search: + specifier: 0.11.0 + version: 0.11.0(xterm@5.1.0) + xterm-addon-search-bar: + specifier: 0.2.0 + version: 0.2.0(xterm-addon-search@0.11.0)(xterm@5.1.0) + xterm-addon-web-links: + specifier: 0.8.0 + version: 0.8.0(xterm@5.1.0) + yup: + specifier: 1.0.0 + version: 1.0.0 + +devDependencies: + '@tailwindcss/forms': + specifier: 0.5.3 + version: 0.5.3(tailwindcss@3.2.7) + '@tailwindcss/line-clamp': + specifier: 0.4.2 + version: 0.4.2(tailwindcss@3.2.7) + '@testing-library/dom': + specifier: 9.0.0 + version: 9.0.0 + '@testing-library/react': + specifier: 14.0.0 + version: 14.0.0(react-dom@18.2.0)(react@18.2.0) + '@testing-library/user-event': + specifier: 14.4.3 + version: 14.4.3(@testing-library/dom@9.0.0) + '@types/debounce': + specifier: 1.2.1 + version: 1.2.1 + '@types/events': + specifier: 3.0.0 + version: 3.0.0 + '@types/node': + specifier: 18.14.1 + version: 18.14.1 + '@types/react': + specifier: 18.0.28 + version: 18.0.28 + '@types/react-dom': + specifier: 18.0.11 + version: 18.0.11 + '@types/styled-components': + specifier: 5.1.26 + version: 5.1.26 + '@typescript-eslint/eslint-plugin': + specifier: 5.53.0 + version: 5.53.0(@typescript-eslint/parser@5.53.0)(eslint@8.34.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: 5.53.0 + version: 5.53.0(eslint@8.34.0)(typescript@4.9.5) + '@vitejs/plugin-react': + specifier: 3.1.0 + version: 3.1.0(vite@4.1.4) + autoprefixer: + specifier: 10.4.13 + version: 10.4.13(postcss@8.4.21) + babel-plugin-styled-components: + specifier: 2.0.7 + version: 2.0.7(styled-components@5.3.6) + babel-plugin-twin: + specifier: 1.1.0 + version: 1.1.0 + cross-env: + specifier: 7.0.3 + version: 7.0.3 + eslint: + specifier: 8.34.0 + version: 8.34.0 + eslint-config-prettier: + specifier: 8.6.0 + version: 8.6.0(eslint@8.34.0) + eslint-plugin-node: + specifier: 11.1.0 + version: 11.1.0(eslint@8.34.0) + eslint-plugin-prettier: + specifier: 4.2.1 + version: 4.2.1(eslint-config-prettier@8.6.0)(eslint@8.34.0)(prettier@2.8.4) + eslint-plugin-react: + specifier: 7.32.2 + version: 7.32.2(eslint@8.34.0) + eslint-plugin-react-hooks: + specifier: 4.6.0 + version: 4.6.0(eslint@8.34.0) + happy-dom: + specifier: 8.7.2 + version: 8.7.2 + laravel-vite-plugin: + specifier: 0.7.4 + version: 0.7.4(vite@4.1.4) + pathe: + specifier: 1.1.0 + version: 1.1.0 + postcss: + specifier: 8.4.21 + version: 8.4.21 + postcss-import: + specifier: 15.1.0 + version: 15.1.0(postcss@8.4.21) + postcss-nesting: + specifier: 11.2.1 + version: 11.2.1(postcss@8.4.21) + postcss-preset-env: + specifier: 8.0.1 + version: 8.0.1(postcss@8.4.21) + prettier: + specifier: 2.8.4 + version: 2.8.4 + prettier-plugin-tailwindcss: + specifier: 0.2.3 + version: 0.2.3(prettier@2.8.4) + rimraf: + specifier: 3.0.2 + version: 3.0.2 + tailwindcss: + specifier: 3.2.7 + version: 3.2.7(postcss@8.4.21) + ts-essentials: + specifier: 9.3.0 + version: 9.3.0(typescript@4.9.5) + twin.macro: + specifier: 2.8.2 + version: 2.8.2 + typescript: + specifier: 4.9.5 + version: 4.9.5 + vite: + specifier: 4.1.4 + version: 4.1.4(@types/node@18.14.1) + vitest: + specifier: 0.28.5 + version: 0.28.5(happy-dom@8.7.2) + +packages: + + /@ampproject/remapping@2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.17 + dev: true + + /@babel/code-frame@7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + + /@babel/compat-data@7.21.0: + resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.21.0: + resolution: {integrity: sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.21.1 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.0) + '@babel/helper-module-transforms': 7.21.2 + '@babel/helpers': 7.21.0 + '@babel/parser': 7.21.2 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2(supports-color@5.5.0) + '@babel/types': 7.21.2 + convert-source-map: 1.9.0 + debug: 4.3.4(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.21.1: + resolution: {integrity: sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 + jsesc: 2.5.2 + + /@babel/helper-annotate-as-pure@7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + + /@babel/helper-compilation-targets@7.20.7(@babel/core@7.21.0): + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.0 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-environment-visitor@7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + + /@babel/helper-function-name@7.21.0: + resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.2 + + /@babel/helper-hoist-variables@7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + + /@babel/helper-module-imports@7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + + /@babel/helper-module-transforms@7.21.2: + resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.20.2 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2(supports-color@5.5.0) + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-plugin-utils@7.20.2: + resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-split-export-declaration@7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + + /@babel/helper-string-parser@7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-identifier@7.19.1: + resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-option@7.21.0: + resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.21.0: + resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2(supports-color@5.5.0) + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + + /@babel/parser@7.21.2: + resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.21.2 + + /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.0): + resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.21.0): + resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/runtime@7.21.0: + resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + + /@babel/template@7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.21.2 + '@babel/types': 7.21.2 + + /@babel/traverse@7.21.2(supports-color@5.5.0): + resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.21.1 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.21.2 + '@babel/types': 7.21.2 + debug: 4.3.4(supports-color@5.5.0) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + /@babel/types@7.21.2: + resolution: {integrity: sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + + /@codemirror/autocomplete@6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2): + resolution: {integrity: sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + dependencies: + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + '@lezer/common': 1.0.2 + dev: false + + /@codemirror/commands@6.2.1: + resolution: {integrity: sha512-FFiNKGuHA5O8uC6IJE5apI5rT9gyjlw4whqy4vlcX0wE/myxL6P1s0upwDhY4HtMWLOwzwsp0ap3bjdQhvfDOA==} + dependencies: + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + '@lezer/common': 1.0.2 + dev: false + + /@codemirror/lang-cpp@6.0.2: + resolution: {integrity: sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==} + dependencies: + '@codemirror/language': 6.6.0 + '@lezer/cpp': 1.1.0 + dev: false + + /@codemirror/lang-css@6.0.2(@codemirror/view@6.9.1)(@lezer/common@1.0.2): + resolution: {integrity: sha512-4V4zmUOl2Glx0GWw0HiO1oGD4zvMlIQ3zx5hXOE6ipCjhohig2bhWRAasrZylH9pRNTcl1VMa59Lsl8lZWlTzw==} + dependencies: + '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@lezer/css': 1.1.1 + transitivePeerDependencies: + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/lang-html@6.4.2: + resolution: {integrity: sha512-bqCBASkteKySwtIbiV/WCtGnn/khLRbbiV5TE+d9S9eQJD7BA4c5dTRm2b3bVmSpilff5EYxvB4PQaZzM/7cNw==} + dependencies: + '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-css': 6.0.2(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-javascript': 6.1.4 + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + '@lezer/common': 1.0.2 + '@lezer/css': 1.1.1 + '@lezer/html': 1.3.3 + dev: false + + /@codemirror/lang-java@6.0.1: + resolution: {integrity: sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==} + dependencies: + '@codemirror/language': 6.6.0 + '@lezer/java': 1.0.3 + dev: false + + /@codemirror/lang-javascript@6.1.4: + resolution: {integrity: sha512-OxLf7OfOZBTMRMi6BO/F72MNGmgOd9B0vetOLvHsDACFXayBzW8fm8aWnDM0yuy68wTK03MBf4HbjSBNRG5q7A==} + dependencies: + '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/language': 6.6.0 + '@codemirror/lint': 6.1.1 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + '@lezer/common': 1.0.2 + '@lezer/javascript': 1.4.1 + dev: false + + /@codemirror/lang-json@6.0.1: + resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==} + dependencies: + '@codemirror/language': 6.6.0 + '@lezer/json': 1.0.0 + dev: false + + /@codemirror/lang-lezer@6.0.1: + resolution: {integrity: sha512-WHwjI7OqKFBEfkunohweqA5B/jIlxaZso6Nl3weVckz8EafYbPZldQEKSDb4QQ9H9BUkle4PVELP4sftKoA0uQ==} + dependencies: + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@lezer/common': 1.0.2 + '@lezer/lezer': 1.1.0 + dev: false + + /@codemirror/lang-markdown@6.1.0: + resolution: {integrity: sha512-HQDJg1Js19fPKKsI3Rp1X0J6mxyrRy2NX6+Evh0+/jGm6IZHL5ygMGKBYNWKXodoDQFvgdofNRG33gWOwV59Ag==} + dependencies: + '@codemirror/lang-html': 6.4.2 + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + '@lezer/common': 1.0.2 + '@lezer/markdown': 1.0.2 + dev: false + + /@codemirror/lang-php@6.0.1: + resolution: {integrity: sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==} + dependencies: + '@codemirror/lang-html': 6.4.2 + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@lezer/common': 1.0.2 + '@lezer/php': 1.0.1 + dev: false + + /@codemirror/lang-python@6.1.1(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2): + resolution: {integrity: sha512-AddGMIKUssUAqaDKoxKWA5GAzy/CVE0eSY7/ANgNzdS1GYBkp6N49XKEyMElkuN04UsZ+bTIQdj+tVV75NMwJw==} + dependencies: + '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/language': 6.6.0 + '@lezer/python': 1.1.2 + transitivePeerDependencies: + - '@codemirror/state' + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/lang-rust@6.0.1: + resolution: {integrity: sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==} + dependencies: + '@codemirror/language': 6.6.0 + '@lezer/rust': 1.0.0 + dev: false + + /@codemirror/lang-sql@6.4.0(@codemirror/view@6.9.1)(@lezer/common@1.0.2): + resolution: {integrity: sha512-UWGK1+zc9+JtkiT+XxHByp4N6VLgLvC2x0tIudrJG26gyNtn0hWOVoB0A8kh/NABPWkKl3tLWDYf2qOBJS9Zdw==} + dependencies: + '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + transitivePeerDependencies: + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/lang-wast@6.0.1: + resolution: {integrity: sha512-sQLsqhRjl2MWG3rxZysX+2XAyed48KhLBHLgq9xcKxIJu3npH/G+BIXW5NM5mHeDUjG0jcGh9BcjP0NfMStuzA==} + dependencies: + '@codemirror/language': 6.6.0 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@codemirror/lang-xml@6.0.2(@codemirror/view@6.9.1): + resolution: {integrity: sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==} + dependencies: + '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/language': 6.6.0 + '@codemirror/state': 6.2.0 + '@lezer/common': 1.0.2 + '@lezer/xml': 1.0.1 + transitivePeerDependencies: + - '@codemirror/view' + dev: false + + /@codemirror/language-data@6.1.0(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2): + resolution: {integrity: sha512-g9V23fuLRI9AEbpM6bDy1oquqgpFlIDHTihUhL21NPmxp+x67ZJbsKk+V71W7/Bj8SCqEO1PtqQA/tDGgt1nfw==} + dependencies: + '@codemirror/lang-cpp': 6.0.2 + '@codemirror/lang-css': 6.0.2(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-html': 6.4.2 + '@codemirror/lang-java': 6.0.1 + '@codemirror/lang-javascript': 6.1.4 + '@codemirror/lang-json': 6.0.1 + '@codemirror/lang-markdown': 6.1.0 + '@codemirror/lang-php': 6.0.1 + '@codemirror/lang-python': 6.1.1(@codemirror/state@6.2.0)(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-rust': 6.0.1 + '@codemirror/lang-sql': 6.4.0(@codemirror/view@6.9.1)(@lezer/common@1.0.2) + '@codemirror/lang-wast': 6.0.1 + '@codemirror/lang-xml': 6.0.2(@codemirror/view@6.9.1) + '@codemirror/language': 6.6.0 + '@codemirror/legacy-modes': 6.3.1 + transitivePeerDependencies: + - '@codemirror/state' + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/language@6.6.0: + resolution: {integrity: sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==} + dependencies: + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + '@lezer/common': 1.0.2 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + style-mod: 4.0.0 + dev: false + + /@codemirror/legacy-modes@6.3.1: + resolution: {integrity: sha512-icXmCs4Mhst2F8mE0TNpmG6l7YTj1uxam3AbZaFaabINH5oWAdg2CfR/PVi+d/rqxJ+TuTnvkKK5GILHrNThtw==} + dependencies: + '@codemirror/language': 6.6.0 + dev: false + + /@codemirror/lint@6.1.1: + resolution: {integrity: sha512-e+M543x0NVHGayNHQzLP4XByJsvbu/ojY6+0VF2Y4Uu66Rt1nADuxNflZwECLf7gS009smIsptSUa6bUj/U/rw==} + dependencies: + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + crelt: 1.0.5 + dev: false + + /@codemirror/search@6.2.3: + resolution: {integrity: sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==} + dependencies: + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.9.1 + crelt: 1.0.5 + dev: false + + /@codemirror/state@6.2.0: + resolution: {integrity: sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==} + dev: false + + /@codemirror/view@6.9.1: + resolution: {integrity: sha512-bzfSjJn9dAADVpabLKWKNmMG4ibyTV2e3eOGowjElNPTdTkSbi6ixPYHm2u0ADcETfKsi2/R84Rkmi91dH9yEg==} + dependencies: + '@codemirror/state': 6.2.0 + style-mod: 4.0.0 + w3c-keyname: 2.2.6 + dev: false + + /@csstools/cascade-layer-name-parser@1.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0): + resolution: {integrity: sha512-SAAi5DpgJJWkfTvWSaqkgyIsTawa83hMwKrktkj6ra2h+q6ZN57vOGZ6ySHq6RSo+CbP64fA3aPChPBRDDUgtw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.0.0 + '@csstools/css-tokenizer': ^2.0.0 + dependencies: + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + dev: true + + /@csstools/color-helpers@1.0.0: + resolution: {integrity: sha512-tgqtiV8sU/VaWYjOB3O7PWs7HR/MmOLl2kTYRW2qSsTSEniJq7xmyAYFB1LPpXvvQcE5u2ih2dK9fyc8BnrAGQ==} + engines: {node: ^14 || ^16 || >=18} + dev: true + + /@csstools/css-calc@1.0.0(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0): + resolution: {integrity: sha512-Xw0b/Jr+vLGGYD8cxsGWPaY5n1GtVC6G4tcga+eZPXZzRjjZHorPwW739UgtXzL2Da1RLxNE73c0r/KvmizPsw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.0.1 + '@csstools/css-tokenizer': ^2.0.1 + dependencies: + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + dev: true + + /@csstools/css-parser-algorithms@2.0.1(@csstools/css-tokenizer@2.1.0): + resolution: {integrity: sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-tokenizer': ^2.0.0 + dependencies: + '@csstools/css-tokenizer': 2.1.0 + dev: true + + /@csstools/css-tokenizer@2.1.0: + resolution: {integrity: sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==} + engines: {node: ^14 || ^16 || >=18} + dev: true + + /@csstools/media-query-list-parser@2.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0): + resolution: {integrity: sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.0.0 + '@csstools/css-tokenizer': ^2.0.0 + dependencies: + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + dev: true + + /@csstools/postcss-cascade-layers@3.0.1(postcss@8.4.21): + resolution: {integrity: sha512-dD8W98dOYNOH/yX4V4HXOhfCOnvVAg8TtsL+qCGNoKXuq5z2C/d026wGWgySgC8cajXXo/wNezS31Glj5GcqrA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.1(postcss-selector-parser@6.0.11)(postcss@8.4.21) + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /@csstools/postcss-color-function@2.1.0(postcss@8.4.21): + resolution: {integrity: sha512-XBoCClLyWchlYGHGlmMOa6M2UXZNrZm63HVfsvgD/z1RPm/s3+FhHyT6VkDo+OvEBPhCgn6xz4IeCu4pRctKDQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/color-helpers': 1.0.0 + '@csstools/postcss-progressive-custom-properties': 2.1.0(postcss@8.4.21) + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-font-format-keywords@2.0.2(postcss@8.4.21): + resolution: {integrity: sha512-iKYZlIs6JsNT7NKyRjyIyezTCHLh4L4BBB3F5Nx7Dc4Z/QmBgX+YJFuUSar8IM6KclGiAUFGomXFdYxAwJydlA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-hwb-function@2.1.1(postcss@8.4.21): + resolution: {integrity: sha512-XijKzdxBdH2hU6IcPWmnaU85FKEF1XE5hGy0d6dQC6XznFUIRu1T4uebL3krayX40m4xIcxfCBsQm5zphzVrtg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/color-helpers': 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-ic-unit@2.0.2(postcss@8.4.21): + resolution: {integrity: sha512-N84qGTJkfLTPj2qOG5P4CIqGjpZBbjOEMKMn+UjO5wlb9lcBTfBsxCF0lQsFdWJUzBHYFOz19dL66v71WF3Pig==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.1.0(postcss@8.4.21) + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-is-pseudo-class@3.1.1(postcss@8.4.21): + resolution: {integrity: sha512-hhiacuby4YdUnnxfCYCRMBIobyJImozf0u+gHSbQ/tNOdwvmrZtVROvgW7zmfYuRkHVDNZJWZslq2v5jOU+j/A==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.1(postcss-selector-parser@6.0.11)(postcss@8.4.21) + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /@csstools/postcss-logical-float-and-clear@1.0.1(postcss@8.4.21): + resolution: {integrity: sha512-eO9z2sMLddvlfFEW5Fxbjyd03zaO7cJafDurK4rCqyRt9P7aaWwha0LcSzoROlcZrw1NBV2JAp2vMKfPMQO1xw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /@csstools/postcss-logical-resize@1.0.1(postcss@8.4.21): + resolution: {integrity: sha512-x1ge74eCSvpBkDDWppl+7FuD2dL68WP+wwP2qvdUcKY17vJksz+XoE1ZRV38uJgS6FNUwC0AxrPW5gy3MxsDHQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-logical-viewport-units@1.0.2(postcss@8.4.21): + resolution: {integrity: sha512-nnKFywBqRMYjv5jyjSplD/nbAnboUEGFfdxKw1o34Y1nvycgqjQavhKkmxbORxroBBIDwC5y6SfgENcPPUcOxQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/css-tokenizer': 2.1.0 + postcss: 8.4.21 + dev: true + + /@csstools/postcss-media-queries-aspect-ratio-number-values@1.0.1(postcss@8.4.21): + resolution: {integrity: sha512-V9yQqXdje6OfqDf6EL5iGOpi6N0OEczwYK83rql9UapQwFEryXlAehR5AqH8QqLYb6+y31wUXK6vMxCp0920Zg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + '@csstools/media-query-list-parser': 2.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0) + postcss: 8.4.21 + dev: true + + /@csstools/postcss-nested-calc@2.0.2(postcss@8.4.21): + resolution: {integrity: sha512-jbwrP8rN4e7LNaRcpx3xpMUjhtt34I9OV+zgbcsYAAk6k1+3kODXJBf95/JMYWhu9g1oif7r06QVUgfWsKxCFw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-normalize-display-values@2.0.1(postcss@8.4.21): + resolution: {integrity: sha512-TQT5g3JQ5gPXC239YuRK8jFceXF9d25ZvBkyjzBGGoW5st5sPXFVQS8OjYb9IJ/K3CdfK4528y483cgS2DJR/w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-oklab-function@2.1.0(postcss@8.4.21): + resolution: {integrity: sha512-U/odSNjOVhagNRu+RDaNVbn8vaqA9GyCOoneQA2je7697KOrtRDc7/POrYsP7QioO2aaezDzKNX02wBzc99fkQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/color-helpers': 1.0.0 + '@csstools/postcss-progressive-custom-properties': 2.1.0(postcss@8.4.21) + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-progressive-custom-properties@2.1.0(postcss@8.4.21): + resolution: {integrity: sha512-tRX1rinsXajZlc4WiU7s9Y6O9EdSHScT997zDsvDUjQ1oZL2nvnL6Bt0s9KyQZZTdC3lrG2PIdBqdOIWXSEPlQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-scope-pseudo-class@2.0.2(postcss@8.4.21): + resolution: {integrity: sha512-6Pvo4uexUCXt+Hz5iUtemQAcIuCYnL+ePs1khFR6/xPgC92aQLJ0zGHonWoewiBE+I++4gXK3pr+R1rlOFHe5w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /@csstools/postcss-stepped-value-functions@2.1.0(postcss@8.4.21): + resolution: {integrity: sha512-CkEo9BF8fQeMoXW3biXjlgTLY7PA4UFihn6leq7hPoRzIguLUI0WZIVgsITGXfX8LXmkhCSTjXO2DLYu/LUixQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/css-calc': 1.0.0(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0) + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + postcss: 8.4.21 + dev: true + + /@csstools/postcss-text-decoration-shorthand@2.2.1(postcss@8.4.21): + resolution: {integrity: sha512-Ow6/cWWdjjVvA83mkm3kLRvvWsbzoe1AbJCxkpC+c9ibUjyS8pifm+LpZslQUKcxRVQ69ztKHDBEbFGTDhNeUw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/color-helpers': 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-trigonometric-functions@2.0.1(postcss@8.4.21): + resolution: {integrity: sha512-uGmmVWGHozyWe6+I4w321fKUC034OB1OYW0ZP4ySHA23n+r9y93K+1yrmW+hThpSfApKhaWySoD4I71LLlFUYQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-unset-value@2.0.1(postcss@8.4.21): + resolution: {integrity: sha512-oJ9Xl29/yU8U7/pnMJRqAZd4YXNCfGEdcP4ywREuqm/xMqcgDNDppYRoCGDt40aaZQIEKBS79LytUDN/DHf0Ew==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /@csstools/selector-specificity@2.1.1(postcss-selector-parser@6.0.11)(postcss@8.4.21): + resolution: {integrity: sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + postcss-selector-parser: ^6.0.10 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /@emotion/babel-plugin@11.10.6: + resolution: {integrity: sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==} + dependencies: + '@babel/helper-module-imports': 7.18.6 + '@babel/runtime': 7.21.0 + '@emotion/hash': 0.9.0 + '@emotion/memoize': 0.8.0 + '@emotion/serialize': 1.1.1 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.1.3 + dev: false + + /@emotion/cache@11.10.5: + resolution: {integrity: sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==} + dependencies: + '@emotion/memoize': 0.8.0 + '@emotion/sheet': 1.2.1 + '@emotion/utils': 1.2.0 + '@emotion/weak-memoize': 0.3.0 + stylis: 4.1.3 + dev: false + + /@emotion/hash@0.9.0: + resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==} + dev: false + + /@emotion/is-prop-valid@0.8.8: + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + requiresBuild: true + dependencies: + '@emotion/memoize': 0.7.4 + dev: false + optional: true + + /@emotion/is-prop-valid@1.2.0: + resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==} + dependencies: + '@emotion/memoize': 0.8.0 + + /@emotion/memoize@0.7.4: + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + requiresBuild: true + dev: false + optional: true + + /@emotion/memoize@0.8.0: + resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==} + + /@emotion/react@11.10.6(@types/react@18.0.28)(react@18.2.0): + resolution: {integrity: sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@emotion/babel-plugin': 11.10.6 + '@emotion/cache': 11.10.5 + '@emotion/serialize': 1.1.1 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) + '@emotion/utils': 1.2.0 + '@emotion/weak-memoize': 0.3.0 + '@types/react': 18.0.28 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + dev: false + + /@emotion/serialize@1.1.1: + resolution: {integrity: sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==} + dependencies: + '@emotion/hash': 0.9.0 + '@emotion/memoize': 0.8.0 + '@emotion/unitless': 0.8.0 + '@emotion/utils': 1.2.0 + csstype: 3.1.1 + dev: false + + /@emotion/sheet@1.2.1: + resolution: {integrity: sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==} + dev: false + + /@emotion/stylis@0.8.5: + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} + + /@emotion/unitless@0.7.5: + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + /@emotion/unitless@0.8.0: + resolution: {integrity: sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==} + dev: false + + /@emotion/use-insertion-effect-with-fallbacks@1.0.0(react@18.2.0): + resolution: {integrity: sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + dev: false + + /@emotion/utils@1.2.0: + resolution: {integrity: sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==} + dev: false + + /@emotion/weak-memoize@0.3.0: + resolution: {integrity: sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==} + dev: false + + /@esbuild/android-arm64@0.16.17: + resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.16.17: + resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.16.17: + resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.16.17: + resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.16.17: + resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.16.17: + resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.16.17: + resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.16.17: + resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.16.17: + resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.16.17: + resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.16.17: + resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.16.17: + resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.16.17: + resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.16.17: + resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.16.17: + resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.16.17: + resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.16.17: + resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.16.17: + resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.16.17: + resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.16.17: + resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.16.17: + resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.16.17: + resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc@1.4.1: + resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4(supports-color@5.5.0) + espree: 9.4.1 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@floating-ui/core@1.2.1: + resolution: {integrity: sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg==} + dev: false + + /@floating-ui/dom@1.2.1: + resolution: {integrity: sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==} + dependencies: + '@floating-ui/core': 1.2.1 + dev: false + + /@floating-ui/react-dom-interactions@0.13.3(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-AnCW06eIZxzD/Hl1Qbi2JkQRU5KpY7Dn81k3xRfbvs+HylhB+t3x88/GNKLK39mMTlJ/ylxm5prUpiLrTWvifQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/react-dom': 1.3.0(react-dom@18.2.0)(react@18.2.0) + aria-hidden: 1.2.2(@types/react@18.0.28)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.1.1 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@floating-ui/react-dom@1.3.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@flyyer/use-fit-text@3.0.1(react@18.2.0): + resolution: {integrity: sha512-bE51XCTURJrRDUCkWUsRKNT8vhzl0Ivar8T2yD43MTE3Q+fzDd+iZxy77HrqycwG35ykQdjua3cRZkDhznNboA==} + peerDependencies: + react: '>=16.8.0' + dependencies: + dequal: 2.0.3 + react: 18.2.0 + dev: false + + /@fortawesome/fontawesome-common-types@6.3.0: + resolution: {integrity: sha512-4BC1NMoacEBzSXRwKjZ/X/gmnbp/HU5Qqat7E8xqorUtBFZS+bwfGH5/wqOC2K6GV0rgEobp3OjGRMa5fK9pFg==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + + /@fortawesome/fontawesome-svg-core@6.3.0: + resolution: {integrity: sha512-uz9YifyKlixV6AcKlOX8WNdtF7l6nakGyLYxYaCa823bEBqyj/U2ssqtctO38itNEwXb8/lMzjdoJ+aaJuOdrw==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.3.0 + dev: false + + /@fortawesome/free-brands-svg-icons@6.3.0: + resolution: {integrity: sha512-xI0c+a8xnKItAXCN8rZgCNCJQiVAd2Y7p9e2ND6zN3J3ekneu96qrePieJ7yA7073C1JxxoM3vH1RU7rYsaj8w==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.3.0 + dev: false + + /@fortawesome/free-solid-svg-icons@6.3.0: + resolution: {integrity: sha512-x5tMwzF2lTH8pyv8yeZRodItP2IVlzzmBuD1M7BjawWgg9XAvktqJJ91Qjgoaf8qJpHQ8FEU9VxRfOkLhh86QA==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.3.0 + dev: false + + /@fortawesome/react-fontawesome@0.2.0(@fortawesome/fontawesome-svg-core@6.3.0)(react@18.2.0): + resolution: {integrity: sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==} + peerDependencies: + '@fortawesome/fontawesome-svg-core': ~1 || ~6 + react: '>=16.3' + dependencies: + '@fortawesome/fontawesome-svg-core': 6.3.0 + prop-types: 15.8.1 + react: 18.2.0 + dev: false + + /@headlessui/react@1.7.11(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-EaDbVgcyiylhtskZZf4Qb/JiiByY7cYbd0qgZ9xm2pm2X7hKojG0P4TaQYKgPOV3vojPhd/pZyQh3nmRkkcSyw==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + client-only: 0.0.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@heroicons/react@1.0.6(react@18.2.0): + resolution: {integrity: sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==} + peerDependencies: + react: '>= 16' + dependencies: + react: 18.2.0 + dev: false + + /@humanwhocodes/config-array@0.11.8: + resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/gen-mapping@0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/gen-mapping@0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.17 + + /@jridgewell/resolve-uri@3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + + /@jridgewell/sourcemap-codec@1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + + /@jridgewell/trace-mapping@0.3.17: + resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + + /@lezer/common@1.0.2: + resolution: {integrity: sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==} + dev: false + + /@lezer/cpp@1.1.0: + resolution: {integrity: sha512-zUHrjNFuY/DOZCkOBJ6qItQIkcopHM/Zv/QOE0a4XNG3HDNahxTNu5fQYl8dIuKCpxCqRdMl5cEwl5zekFc7BA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/css@1.1.1: + resolution: {integrity: sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/highlight@1.1.3: + resolution: {integrity: sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==} + dependencies: + '@lezer/common': 1.0.2 + dev: false + + /@lezer/html@1.3.3: + resolution: {integrity: sha512-04Fyvu66DjV2EjhDIG1kfDdktn5Pfw56SXPrzKNQH5B2m7BDfc6bDsz+ZJG8dLS3kIPEKbyyq1Sm2/kjeG0+AA==} + dependencies: + '@lezer/common': 1.0.2 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/java@1.0.3: + resolution: {integrity: sha512-kKN17wmgP1cgHb8juR4pwVSPMKkDMzY/lAPbBsZ1fpXwbk2sg3N1kIrf0q+LefxgrANaQb/eNO7+m2QPruTFng==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/javascript@1.4.1: + resolution: {integrity: sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/json@1.0.0: + resolution: {integrity: sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/lezer@1.1.0: + resolution: {integrity: sha512-XTomM3C2MzHNuZwjYbyYZ44IRV6rHIOvi++yAD1O4djlDoKAnikx3BFoREK2g/z8zUIc/kyWuZO9W9xN4/OR1g==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/lr@1.3.3: + resolution: {integrity: sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==} + dependencies: + '@lezer/common': 1.0.2 + dev: false + + /@lezer/markdown@1.0.2: + resolution: {integrity: sha512-8CY0OoZ6V5EzPjSPeJ4KLVbtXdLBd8V6sRCooN5kHnO28ytreEGTyrtU/zUwo/XLRzGr/e1g44KlzKi3yWGB5A==} + dependencies: + '@lezer/common': 1.0.2 + '@lezer/highlight': 1.1.3 + dev: false + + /@lezer/php@1.0.1: + resolution: {integrity: sha512-aqdCQJOXJ66De22vzdwnuC502hIaG9EnPK2rSi+ebXyUd+j7GAX1mRjWZOVOmf3GST1YUfUCu6WXDiEgDGOVwA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/python@1.1.2: + resolution: {integrity: sha512-ukm4VhDasFX7/9BUYHTyUNXH0xQ5B7/QBlZD8P51+dh6GtXRSCQqNxloez5d+MxVb2Sg+31S8E/33qoFREfkpA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/rust@1.0.0: + resolution: {integrity: sha512-IpGAxIjNxYmX9ra6GfQTSPegdCAWNeq23WNmrsMMQI7YNSvKtYxO4TX5rgZUmbhEucWn0KTBMeDEPXg99YKtTA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@lezer/xml@1.0.1: + resolution: {integrity: sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.3 + dev: false + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@preact/signals-core@1.2.3: + resolution: {integrity: sha512-Kui4p7PMcEQevBgsTO0JBo3gyQ88Q3qzEvsVCuSp11t0JcN4DmGCTJcGRVSCq7Bn7lGxJBO+57jNSzDoDJ+QmA==} + dev: false + + /@preact/signals-react@1.2.2(react@18.2.0): + resolution: {integrity: sha512-GoESQ9n1bns2FD+8yqH7lBvQMavboKLCNEW+s0hs3Wcp5B1VHvVxwJo6aFs6rpxoh1/q8Tvwbi4vIeehBD2mzA==} + peerDependencies: + react: 17.x || 18.x + dependencies: + '@preact/signals-core': 1.2.3 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /@remix-run/router@1.3.2: + resolution: {integrity: sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==} + engines: {node: '>=14'} + dev: false + + /@tailwindcss/forms@0.5.3(tailwindcss@3.2.7): + resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 3.2.7(postcss@8.4.21) + dev: true + + /@tailwindcss/line-clamp@0.4.2(tailwindcss@3.2.7): + resolution: {integrity: sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + dependencies: + tailwindcss: 3.2.7(postcss@8.4.21) + dev: true + + /@testing-library/dom@9.0.0: + resolution: {integrity: sha512-+/TLgKNFsYUshOY/zXsQOk+PlFQK+eyJ9T13IDVNJEi+M+Un7xlJK+FZKkbGSnf0+7E1G6PlDhkSYQ/GFiruBQ==} + engines: {node: '>=14'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/runtime': 7.21.0 + '@types/aria-query': 5.0.1 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.4.4 + pretty-format: 27.5.1 + dev: true + + /@testing-library/react@14.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==} + engines: {node: '>=14'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@babel/runtime': 7.21.0 + '@testing-library/dom': 9.0.0 + '@types/react-dom': 18.0.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@testing-library/user-event@14.4.3(@testing-library/dom@9.0.0): + resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + dependencies: + '@testing-library/dom': 9.0.0 + dev: true + + /@types/aria-query@5.0.1: + resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} + dev: true + + /@types/chai-subset@1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.4 + dev: true + + /@types/chai@4.3.4: + resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} + dev: true + + /@types/debounce@1.2.1: + resolution: {integrity: sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==} + dev: true + + /@types/events@3.0.0: + resolution: {integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==} + dev: true + + /@types/hoist-non-react-statics@3.3.1: + resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} + dependencies: + '@types/react': 18.0.28 + hoist-non-react-statics: 3.3.2 + + /@types/json-schema@7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/node@18.14.1: + resolution: {integrity: sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==} + dev: true + + /@types/parse-json@4.0.0: + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + + /@types/prop-types@15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + + /@types/react-dom@18.0.11: + resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==} + dependencies: + '@types/react': 18.0.28 + + /@types/react-transition-group@4.4.5: + resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} + dependencies: + '@types/react': 18.0.28 + dev: false + + /@types/react@18.0.28: + resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.1 + + /@types/scheduler@0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + + /@types/semver@7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + + /@types/styled-components@5.1.26: + resolution: {integrity: sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==} + dependencies: + '@types/hoist-non-react-statics': 3.3.1 + '@types/react': 18.0.28 + csstype: 3.1.1 + + /@typescript-eslint/eslint-plugin@5.53.0(@typescript-eslint/parser@5.53.0)(eslint@8.34.0)(typescript@4.9.5): + resolution: {integrity: sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.53.0(eslint@8.34.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.53.0 + '@typescript-eslint/type-utils': 5.53.0(eslint@8.34.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.53.0(eslint@8.34.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.34.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + regexpp: 3.2.0 + semver: 7.3.8 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.53.0(eslint@8.34.0)(typescript@4.9.5): + resolution: {integrity: sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.53.0 + '@typescript-eslint/types': 5.53.0 + '@typescript-eslint/typescript-estree': 5.53.0(typescript@4.9.5) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.34.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.53.0: + resolution: {integrity: sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.53.0 + '@typescript-eslint/visitor-keys': 5.53.0 + dev: true + + /@typescript-eslint/type-utils@5.53.0(eslint@8.34.0)(typescript@4.9.5): + resolution: {integrity: sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.53.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.53.0(eslint@8.34.0)(typescript@4.9.5) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.34.0 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.53.0: + resolution: {integrity: sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.53.0(typescript@4.9.5): + resolution: {integrity: sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.53.0 + '@typescript-eslint/visitor-keys': 5.53.0 + debug: 4.3.4(supports-color@5.5.0) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.53.0(eslint@8.34.0)(typescript@4.9.5): + resolution: {integrity: sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.53.0 + '@typescript-eslint/types': 5.53.0 + '@typescript-eslint/typescript-estree': 5.53.0(typescript@4.9.5) + eslint: 8.34.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0(eslint@8.34.0) + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.53.0: + resolution: {integrity: sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.53.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@vitejs/plugin-react@3.1.0(vite@4.1.4): + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.1.0-beta.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.0) + '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.0) + magic-string: 0.27.0 + react-refresh: 0.14.0 + vite: 4.1.4(@types/node@18.14.1) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/expect@0.28.5: + resolution: {integrity: sha512-gqTZwoUTwepwGIatnw4UKpQfnoyV0Z9Czn9+Lo2/jLIt4/AXLTn+oVZxlQ7Ng8bzcNkR+3DqLJ08kNr8jRmdNQ==} + dependencies: + '@vitest/spy': 0.28.5 + '@vitest/utils': 0.28.5 + chai: 4.3.7 + dev: true + + /@vitest/runner@0.28.5: + resolution: {integrity: sha512-NKkHtLB+FGjpp5KmneQjTcPLWPTDfB7ie+MmF1PnUBf/tGe2OjGxWyB62ySYZ25EYp9krR5Bw0YPLS/VWh1QiA==} + dependencies: + '@vitest/utils': 0.28.5 + p-limit: 4.0.0 + pathe: 1.1.0 + dev: true + + /@vitest/spy@0.28.5: + resolution: {integrity: sha512-7if6rsHQr9zbmvxN7h+gGh2L9eIIErgf8nSKYDlg07HHimCxp4H6I/X/DPXktVPPLQfiZ1Cw2cbDIx9fSqDjGw==} + dependencies: + tinyspy: 1.1.1 + dev: true + + /@vitest/utils@0.28.5: + resolution: {integrity: sha512-UyZdYwdULlOa4LTUSwZ+Paz7nBHGTT72jKwdFSV4IjHF1xsokp+CabMdhjvVhYwkLfO88ylJT46YMilnkSARZA==} + dependencies: + cli-truncate: 3.1.0 + diff: 5.1.0 + loupe: 2.3.6 + picocolors: 1.0.0 + pretty-format: 27.5.1 + dev: true + + /acorn-jsx@5.3.2(acorn@8.8.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.2 + dev: true + + /acorn-node@1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + dev: true + + /acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn@8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-hidden@1.2.2(@types/react@18.0.28)(react@18.2.0): + resolution: {integrity: sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.9.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + tslib: 2.5.0 + dev: false + + /aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + dependencies: + deep-equal: 2.2.0 + dev: true + + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + get-intrinsic: 1.2.0 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.tosorted@1.1.1: + resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.0 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /autoprefixer@10.4.13(postcss@8.4.21): + resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001457 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /axios@0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + dev: false + + /babel-plugin-macros@2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + dependencies: + '@babel/runtime': 7.21.0 + cosmiconfig: 6.0.0 + resolve: 1.22.1 + dev: true + + /babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.21.0 + cosmiconfig: 7.1.0 + resolve: 1.22.1 + dev: false + + /babel-plugin-styled-components@2.0.7(styled-components@5.3.6): + resolution: {integrity: sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==} + peerDependencies: + styled-components: '>= 2' + dependencies: + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + babel-plugin-syntax-jsx: 6.18.0 + lodash: 4.17.21 + picomatch: 2.3.1 + styled-components: 5.3.6(react-dom@18.2.0)(react-is@17.0.2)(react@18.2.0) + + /babel-plugin-syntax-jsx@6.18.0: + resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==} + + /babel-plugin-twin@1.1.0: + resolution: {integrity: sha512-x4AZ7JSh8MmC6uEN41nrU021jLA0B4E1UD7AI0KJOtAwvTH1wEMLugW5SyY4m/CWDtsqZc1fqT1lkDoO2rdmHg==} + dependencies: + '@babel/template': 7.20.7 + dev: true + + /babel-runtime@6.26.0: + resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==} + dependencies: + core-js: 2.6.12 + regenerator-runtime: 0.11.1 + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /boring-avatars@1.7.0: + resolution: {integrity: sha512-ZNHd8J7C/V0IjQMGQowLJ5rScEFU23WxePigH6rqKcT2Esf0qhYvYxw8s9i3srmlfCnCV00ddBjaoGey1eNOfA==} + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001457 + electron-to-chromium: 1.4.309 + node-releases: 2.0.10 + update-browserslist-db: 1.0.10(browserslist@4.21.5) + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + + /caniuse-lite@1.0.30001457: + resolution: {integrity: sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==} + dev: true + + /chai@4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.3 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chart.js@3.9.1: + resolution: {integrity: sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==} + dev: false + + /check-error@1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /classnames@2.3.2: + resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} + dev: false + + /clean-set@1.1.2: + resolution: {integrity: sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==} + dev: true + + /cli-truncate@3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + slice-ansi: 5.0.0 + string-width: 5.1.2 + dev: true + + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: true + + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: true + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: true + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + /commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + /copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + dependencies: + toggle-selection: 1.0.6 + dev: false + + /core-js@2.6.12: + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + requiresBuild: true + dev: false + + /cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + /crelt@1.0.5: + resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==} + dev: false + + /cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + + /cross-fetch@3.1.5: + resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} + dependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /css-blank-pseudo@5.0.2(postcss@8.4.21): + resolution: {integrity: sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + /css-color-names@0.0.4: + resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} + dev: true + + /css-has-pseudo@5.0.2(postcss@8.4.21): + resolution: {integrity: sha512-q+U+4QdwwB7T9VEW/LyO6CFrLAeLqOykC5mDqJXc7aKZAhDbq7BvGT13VGJe+IwBfdN2o3Xdw2kJ5IxwV1Sc9Q==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.1(postcss-selector-parser@6.0.11)(postcss@8.4.21) + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + dev: true + + /css-prefers-color-scheme@8.0.2(postcss@8.4.21): + resolution: {integrity: sha512-OvFghizHJ45x7nsJJUSYLyQNTzsCU8yWjxAc/nhPQg1pbs18LMoET8N3kOweFDPy0JV0OSXN2iqRFhPBHYOeMA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + + /css-unit-converter@1.1.2: + resolution: {integrity: sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==} + dev: true + + /css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true + + /cssdb@7.4.1: + resolution: {integrity: sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==} + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /csstype@3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + + /date-fns@2.29.3: + resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} + engines: {node: '>=0.11'} + dev: false + + /debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + dev: false + + /debug@4.3.4(supports-color@5.5.0): + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + supports-color: 5.5.0 + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-equal@2.2.0: + resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} + dependencies: + call-bind: 1.0.2 + es-get-iterator: 1.1.3 + get-intrinsic: 1.2.0 + is-arguments: 1.1.1 + is-array-buffer: 3.0.1 + is-date-object: 1.0.5 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + isarray: 2.0.5 + object-is: 1.1.5 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.9 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge-ts@4.3.0: + resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} + engines: {node: '>=12.4.0'} + dev: false + + /deepmerge@2.2.1: + resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} + engines: {node: '>=0.10.0'} + dev: false + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /defined@1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + + /detective@5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.1 + minimist: 1.2.8 + dev: true + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + + /diff@5.1.0: + resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dev: true + + /dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dependencies: + '@babel/runtime': 7.21.0 + csstype: 3.1.1 + dev: false + + /dset@2.1.0: + resolution: {integrity: sha512-hlQYwNEdW7Qf8zxysy+yN1E8C/SxRst3Z9n+IvXOR35D9bPVwNHhnL8ZBeoZjvinuGrlvGg6pAMDwhmjqFDgjA==} + engines: {node: '>=4'} + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /easy-peasy@5.2.0(@types/react-dom@18.0.11)(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QHRRJTsF15ZLK5vLcCLDF3A7sMrE4+7S/8ggZempfMbXX3xNsH2R3HVA+Xo5rZA65Rg8Zf5X6fflEMGuzfgwTw==} + peerDependencies: + '@types/react': ^16.8 || ^17.0 || ^18.0 + '@types/react-dom': ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + react-native: '>=0.59' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.21.0 + '@types/react': 18.0.28 + '@types/react-dom': 18.0.11 + fast-deep-equal: 3.1.3 + immer: 9.0.19 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + redux: 4.2.1 + redux-thunk: 2.4.2(redux@4.2.1) + ts-toolbelt: 9.6.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /electron-to-chromium@1.4.309: + resolution: {integrity: sha512-U7DTiKe4h+irqBG6h4EZ0XXaZuJj4md3xIXXaGSYhwiumPZ4BSc6rgf9UD0hVUMaeP/jB0q5pKWCPxvhO8fvZA==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + + /es-abstract@1.21.1: + resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.0 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.1 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.10 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + has-symbols: 1.0.3 + is-arguments: 1.1.1 + is-map: 2.0.2 + is-set: 2.0.2 + is-string: 1.0.7 + isarray: 2.0.5 + stop-iteration-iterator: 1.0.0 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.16.17: + resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.16.17 + '@esbuild/android-arm64': 0.16.17 + '@esbuild/android-x64': 0.16.17 + '@esbuild/darwin-arm64': 0.16.17 + '@esbuild/darwin-x64': 0.16.17 + '@esbuild/freebsd-arm64': 0.16.17 + '@esbuild/freebsd-x64': 0.16.17 + '@esbuild/linux-arm': 0.16.17 + '@esbuild/linux-arm64': 0.16.17 + '@esbuild/linux-ia32': 0.16.17 + '@esbuild/linux-loong64': 0.16.17 + '@esbuild/linux-mips64el': 0.16.17 + '@esbuild/linux-ppc64': 0.16.17 + '@esbuild/linux-riscv64': 0.16.17 + '@esbuild/linux-s390x': 0.16.17 + '@esbuild/linux-x64': 0.16.17 + '@esbuild/netbsd-x64': 0.16.17 + '@esbuild/openbsd-x64': 0.16.17 + '@esbuild/sunos-x64': 0.16.17 + '@esbuild/win32-arm64': 0.16.17 + '@esbuild/win32-ia32': 0.16.17 + '@esbuild/win32-x64': 0.16.17 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + /eslint-config-prettier@8.6.0(eslint@8.34.0): + resolution: {integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.34.0 + dev: true + + /eslint-plugin-es@3.0.1(eslint@8.34.0): + resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + eslint: 8.34.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + dev: true + + /eslint-plugin-node@11.1.0(eslint@8.34.0): + resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=5.16.0' + dependencies: + eslint: 8.34.0 + eslint-plugin-es: 3.0.1(eslint@8.34.0) + eslint-utils: 2.1.0 + ignore: 5.2.4 + minimatch: 3.1.2 + resolve: 1.22.1 + semver: 6.3.0 + dev: true + + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.6.0)(eslint@8.34.0)(prettier@2.8.4): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.34.0 + eslint-config-prettier: 8.6.0(eslint@8.34.0) + prettier: 2.8.4 + prettier-linter-helpers: 1.0.0 + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.34.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.34.0 + dev: true + + /eslint-plugin-react@7.32.2(eslint@8.34.0): + resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.1 + doctrine: 2.1.0 + eslint: 8.34.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.3 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + object.hasown: 1.1.2 + object.values: 1.1.6 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.0 + string.prototype.matchall: 4.0.8 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-utils@3.0.0(eslint@8.34.0): + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.34.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys@3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.34.0: + resolution: {integrity: sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4(supports-color@5.5.0) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0(eslint@8.34.0) + eslint-visitor-keys: 3.3.0 + espree: 9.4.1 + esquery: 1.4.2 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.20.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.3.0 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.4.1: + resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.2 + acorn-jsx: 5.3.2(acorn@8.8.2) + eslint-visitor-keys: 3.3.0 + dev: true + + /esquery@1.4.2: + resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: false + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + /fast-diff@1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + + /fast-glob@3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + dev: false + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /follow-redirects@1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /formik@2.2.9(react@18.2.0): + resolution: {integrity: sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==} + peerDependencies: + react: '>=16.8.0' + dependencies: + deepmerge: 2.2.1 + hoist-non-react-statics: 3.3.2 + lodash: 4.17.21 + lodash-es: 4.17.21 + react: 18.2.0 + react-fast-compare: 2.0.4 + tiny-warning: 1.0.3 + tslib: 1.14.1 + dev: false + + /fraction.js@4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: true + + /framer-motion@9.1.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3y4p8xGspkEe7eWPdKncg4IvYeUwvHeky9em6dqKu6gnrmo2iBdrChX0s+emhbv00/VuBhdf33rw38J/cxaBrA==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tslib: 2.5.0 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + dev: false + + /fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /function.prototype.name@1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-func-name@2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true + + /get-intrinsic@1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + /globals@13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true + + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /happy-dom@8.7.2: + resolution: {integrity: sha512-lkm1l7SLNtI9svaU3PflbM8zahYahLrUZf0fZTUkQ8W6bo5gtXjC/2utOkcjpv9rhWTkHFUuDVjAvBWg4ClAxA==} + dependencies: + css.escape: 1.5.1 + he: 1.2.0 + node-fetch: 2.6.9 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + transitivePeerDependencies: + - encoding + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + + /hex-color-regex@1.1.0: + resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==} + dev: true + + /hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + + /hsl-regex@1.0.0: + resolution: {integrity: sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==} + dev: true + + /hsla-regex@1.0.0: + resolution: {integrity: sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==} + dev: true + + /html-parse-stringify@3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + dependencies: + void-elements: 3.1.0 + dev: false + + /html-tags@3.2.0: + resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==} + engines: {node: '>=8'} + dev: true + + /i18next-http-backend@2.1.1: + resolution: {integrity: sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + + /i18next-multiload-backend-adapter@2.2.0: + resolution: {integrity: sha512-V7or5zeiCMjWC45vFrlkNgef+y4PH9B2PGcoRZnypjnPIn9cFoEEaJoqHXIFnKsZeyjzFDrhAwE57GFyNVyW1g==} + dev: false + + /i18next@22.4.10: + resolution: {integrity: sha512-3EqgGK6fAJRjnGgfkNSStl4mYLCjUoJID338yVyLMj5APT67HUtWoqSayZewiiC5elzMUB1VEUwcmSCoeQcNEA==} + dependencies: + '@babel/runtime': 7.21.0 + dev: false + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /immer@9.0.19: + resolution: {integrity: sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==} + dev: false + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-array-buffer@3.0.1: + resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-typed-array: 1.1.10 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-color-stop@1.1.0: + resolution: {integrity: sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==} + dependencies: + css-color-names: 0.0.4 + hex-color-regex: 1.1.0 + hsl-regex: 1.0.0 + hsla-regex: 1.0.0 + rgb-regex: 1.0.1 + rgba-regex: 1.0.0 + dev: true + + /is-core-module@2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-map@2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-set@2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-sdsl@4.3.0: + resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.10 + dev: true + + /jsx-ast-utils@3.3.3: + resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.6 + object.assign: 4.1.4 + dev: true + + /laravel-vite-plugin@0.7.4(vite@4.1.4): + resolution: {integrity: sha512-NlIuXbeuI+4NZzRpWNpGHRVTwuFWessvD7QoD+o2MlyAi7qyUS4J8r4/yTlu1dl9lxcR7iKoYUmHQqZDcrw2KA==} + engines: {node: '>=14'} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + dependencies: + picocolors: 1.0.0 + vite: 4.1.4(@types/node@18.14.1) + vite-plugin-full-reload: 1.0.5(vite@4.1.4) + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} + engines: {node: '>=10'} + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash.flatmap@4.5.0: + resolution: {integrity: sha512-/OcpcAGWlrZyoHGeHh3cAoa6nGdX6QYtmzNP84Jqol6UEQQ2gIaU3H+0eICcjcKGl0/XF8LWOujNn9lffsnaOg==} + dev: true + + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.topath@4.5.2: + resolution: {integrity: sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /loupe@2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lz-string@1.4.4: + resolution: {integrity: sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==} + hasBin: true + dev: true + + /magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + dev: false + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mini-svg-data-uri@1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mlly@1.1.1: + resolution: {integrity: sha512-Jnlh4W/aI4GySPo6+DyTN17Q75KKbLTyFK8BrGhjNP4rxuUjbRWhE6gHg3bs33URWAF44FRm7gdQA348i3XxRw==} + dependencies: + acorn: 8.8.2 + pathe: 1.1.0 + pkg-types: 1.0.2 + ufo: 1.1.0 + dev: true + + /modern-normalize@1.1.0: + resolution: {integrity: sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==} + engines: {node: '>=6'} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /nanoid@3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /nanoid@4.0.1: + resolution: {integrity: sha512-udKGtCCUafD3nQtJg9wBhRP3KMbPglUsgV5JVsXhvyBs/oefqb4sqMEhKBBgqZncYowu58p1prsZQBYvAj/Gww==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + dev: false + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-emoji@1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + dependencies: + lodash: 4.17.21 + dev: true + + /node-fetch@2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + + /node-fetch@2.6.9: + resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /node-releases@2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + dev: true + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /object-is@1.1.5: + resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.6: + resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /object.fromentries@2.0.6: + resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /object.hasown@1.1.2: + resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + dependencies: + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /object.values@1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.18.6 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + /pathe@1.1.0: + resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /pkg-types@1.0.2: + resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.1.1 + pathe: 1.1.0 + dev: true + + /postcss-attribute-case-insensitive@6.0.2(postcss@8.4.21): + resolution: {integrity: sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-clamp@4.1.0(postcss@8.4.21): + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-color-functional-notation@5.0.2(postcss@8.4.21): + resolution: {integrity: sha512-M6ygxWOyd6eWf3sd1Lv8xi4SeF4iBPfJvkfMU4ITh8ExJc1qhbvh/U8Cv/uOvBgUVOMDdScvCdlg8+hREQzs7w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-color-hex-alpha@9.0.2(postcss@8.4.21): + resolution: {integrity: sha512-SfPjgr//VQ/DOCf80STIAsdAs7sbIbxATvVmd+Ec7JvR8onz9pjawhq3BJM3Pie40EE3TyB0P6hft16D33Nlyg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-color-rebeccapurple@8.0.2(postcss@8.4.21): + resolution: {integrity: sha512-xWf/JmAxVoB5bltHpXk+uGRoGFwu4WDAR7210el+iyvTdqiKpDhtcT8N3edXMoVJY0WHFMrKMUieql/wRNiXkw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-custom-media@9.1.2(postcss@8.4.21): + resolution: {integrity: sha512-osM9g4UKq4XKimAC7RAXroqi3BXpxfwTswAJQiZdrBjWGFGEyxQrY5H2eDWI8F+MEvEUfYDxA8scqi3QWROCSw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/cascade-layer-name-parser': 1.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0) + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + '@csstools/media-query-list-parser': 2.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0) + postcss: 8.4.21 + dev: true + + /postcss-custom-properties@13.1.4(postcss@8.4.21): + resolution: {integrity: sha512-iSAdaZrM3KMec8cOSzeTUNXPYDlhqsMJHpt62yrjwG6nAnMtRHPk5JdMzGosBJtqEahDolvD5LNbcq+EZ78o5g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/cascade-layer-name-parser': 1.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0) + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-custom-selectors@7.1.2(postcss@8.4.21): + resolution: {integrity: sha512-jX7VlE3jrgfBIOfxiGNRFq81xUoHSZhvxhQurzE7ZFRv+bUmMwB7/XnA0nNlts2CwNtbXm4Ozy0ZAYKHlCRmBQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/cascade-layer-name-parser': 1.0.1(@csstools/css-parser-algorithms@2.0.1)(@csstools/css-tokenizer@2.1.0) + '@csstools/css-parser-algorithms': 2.0.1(@csstools/css-tokenizer@2.1.0) + '@csstools/css-tokenizer': 2.1.0 + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-dir-pseudo-class@7.0.2(postcss@8.4.21): + resolution: {integrity: sha512-cMnslilYxBf9k3qejnovrUONZx1rXeUZJw06fgIUBzABJe3D2LiLL5WAER7Imt3nrkaIgG05XZBztueLEf5P8w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-double-position-gradients@4.0.2(postcss@8.4.21): + resolution: {integrity: sha512-GXL1RmFREDK4Q9aYvI2RhVrA6a6qqSMQQ5ke8gSH1xgV6exsqbcJpIumC7AOgooH6/WIG3/K/T8xxAiVHy/tJg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.1.0(postcss@8.4.21) + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-focus-visible@8.0.2(postcss@8.4.21): + resolution: {integrity: sha512-f/Vd+EC/GaKElknU59esVcRYr/Y3t1ZAQyL4u2xSOgkDy4bMCmG7VP5cGvj3+BTLNE9ETfEuz2nnt4qkZwTTeA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-focus-within@7.0.2(postcss@8.4.21): + resolution: {integrity: sha512-AHAJ89UQBcqBvFgQJE9XasGuwMNkKsGj4D/f9Uk60jFmEBHpAL14DrnSk3Rj+SwZTr/WUG+mh+Rvf8fid/346w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-font-variant@5.0.0(postcss@8.4.21): + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-gap-properties@4.0.1(postcss@8.4.21): + resolution: {integrity: sha512-V5OuQGw4lBumPlwHWk/PRfMKjaq/LTGR4WDTemIMCaMevArVfCCA9wBJiL1VjDAd+rzuCIlkRoRvDsSiAaZ4Fg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-image-set-function@5.0.2(postcss@8.4.21): + resolution: {integrity: sha512-Sszjwo0ubETX0Fi5MvpYzsONwrsjeabjMoc5YqHvURFItXgIu3HdCjcVuVKGMPGzKRhgaknmdM5uVWInWPJmeg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-import@14.1.0(postcss@8.4.21): + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + dev: true + + /postcss-import@15.1.0(postcss@8.4.21): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + dev: true + + /postcss-initial@4.0.1(postcss@8.4.21): + resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-js@3.0.3: + resolution: {integrity: sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==} + engines: {node: '>=10.0'} + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.21 + dev: true + + /postcss-js@4.0.1(postcss@8.4.21): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.21 + dev: true + + /postcss-lab-function@5.1.0(postcss@8.4.21): + resolution: {integrity: sha512-iZApRTNcpc71uTn7PkzjHtj5cmuZpvu6okX4jHnM5OFi2fG97sodjxkq6SpL65xhW0NviQrAMSX97ntyGVRV0w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/color-helpers': 1.0.0 + '@csstools/postcss-progressive-custom-properties': 2.1.0(postcss@8.4.21) + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-load-config@3.1.4(postcss@8.4.21): + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 1.10.2 + dev: true + + /postcss-logical@6.1.0(postcss@8.4.21): + resolution: {integrity: sha512-qb1+LpClhYjxac8SfOcWotnY3unKZesDqIOm+jnGt8rTl7xaIWpE2bPGZHxflOip1E/4ETo79qlJyRL3yrHn1g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-media-minmax@5.0.0(postcss@8.4.21): + resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-nested@5.0.6(postcss@8.4.21): + resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-nested@6.0.0(postcss@8.4.21): + resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-nesting@11.2.1(postcss@8.4.21): + resolution: {integrity: sha512-E6Jq74Jo/PbRAtZioON54NPhUNJYxVWhwxbweYl1vAoBYuGlDIts5yhtKiZFLvkvwT73e/9nFrW3oMqAtgG+GQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.1(postcss-selector-parser@6.0.11)(postcss@8.4.21) + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-opacity-percentage@1.1.3(postcss@8.4.21): + resolution: {integrity: sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-overflow-shorthand@4.0.1(postcss@8.4.21): + resolution: {integrity: sha512-HQZ0qi/9iSYHW4w3ogNqVNr2J49DHJAl7r8O2p0Meip38jsdnRPgiDW7r/LlLrrMBMe3KHkvNtAV2UmRVxzLIg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-page-break@3.0.4(postcss@8.4.21): + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-place@8.0.1(postcss@8.4.21): + resolution: {integrity: sha512-Ow2LedN8sL4pq8ubukO77phSVt4QyCm35ZGCYXKvRFayAwcpgB0sjNJglDoTuRdUL32q/ZC1VkPBo0AOEr4Uiw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-preset-env@8.0.1(postcss@8.4.21): + resolution: {integrity: sha512-IUbymw0JlUbyVG+I85963PNWgPp3KhnFa1sxU7M/2dGthxV8e297P0VV5W9XcyypoH4hirH2fp1c6fmqh6YnSg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-cascade-layers': 3.0.1(postcss@8.4.21) + '@csstools/postcss-color-function': 2.1.0(postcss@8.4.21) + '@csstools/postcss-font-format-keywords': 2.0.2(postcss@8.4.21) + '@csstools/postcss-hwb-function': 2.1.1(postcss@8.4.21) + '@csstools/postcss-ic-unit': 2.0.2(postcss@8.4.21) + '@csstools/postcss-is-pseudo-class': 3.1.1(postcss@8.4.21) + '@csstools/postcss-logical-float-and-clear': 1.0.1(postcss@8.4.21) + '@csstools/postcss-logical-resize': 1.0.1(postcss@8.4.21) + '@csstools/postcss-logical-viewport-units': 1.0.2(postcss@8.4.21) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 1.0.1(postcss@8.4.21) + '@csstools/postcss-nested-calc': 2.0.2(postcss@8.4.21) + '@csstools/postcss-normalize-display-values': 2.0.1(postcss@8.4.21) + '@csstools/postcss-oklab-function': 2.1.0(postcss@8.4.21) + '@csstools/postcss-progressive-custom-properties': 2.1.0(postcss@8.4.21) + '@csstools/postcss-scope-pseudo-class': 2.0.2(postcss@8.4.21) + '@csstools/postcss-stepped-value-functions': 2.1.0(postcss@8.4.21) + '@csstools/postcss-text-decoration-shorthand': 2.2.1(postcss@8.4.21) + '@csstools/postcss-trigonometric-functions': 2.0.1(postcss@8.4.21) + '@csstools/postcss-unset-value': 2.0.1(postcss@8.4.21) + autoprefixer: 10.4.13(postcss@8.4.21) + browserslist: 4.21.5 + css-blank-pseudo: 5.0.2(postcss@8.4.21) + css-has-pseudo: 5.0.2(postcss@8.4.21) + css-prefers-color-scheme: 8.0.2(postcss@8.4.21) + cssdb: 7.4.1 + postcss: 8.4.21 + postcss-attribute-case-insensitive: 6.0.2(postcss@8.4.21) + postcss-clamp: 4.1.0(postcss@8.4.21) + postcss-color-functional-notation: 5.0.2(postcss@8.4.21) + postcss-color-hex-alpha: 9.0.2(postcss@8.4.21) + postcss-color-rebeccapurple: 8.0.2(postcss@8.4.21) + postcss-custom-media: 9.1.2(postcss@8.4.21) + postcss-custom-properties: 13.1.4(postcss@8.4.21) + postcss-custom-selectors: 7.1.2(postcss@8.4.21) + postcss-dir-pseudo-class: 7.0.2(postcss@8.4.21) + postcss-double-position-gradients: 4.0.2(postcss@8.4.21) + postcss-focus-visible: 8.0.2(postcss@8.4.21) + postcss-focus-within: 7.0.2(postcss@8.4.21) + postcss-font-variant: 5.0.0(postcss@8.4.21) + postcss-gap-properties: 4.0.1(postcss@8.4.21) + postcss-image-set-function: 5.0.2(postcss@8.4.21) + postcss-initial: 4.0.1(postcss@8.4.21) + postcss-lab-function: 5.1.0(postcss@8.4.21) + postcss-logical: 6.1.0(postcss@8.4.21) + postcss-media-minmax: 5.0.0(postcss@8.4.21) + postcss-nesting: 11.2.1(postcss@8.4.21) + postcss-opacity-percentage: 1.1.3(postcss@8.4.21) + postcss-overflow-shorthand: 4.0.1(postcss@8.4.21) + postcss-page-break: 3.0.4(postcss@8.4.21) + postcss-place: 8.0.1(postcss@8.4.21) + postcss-pseudo-class-any-link: 8.0.2(postcss@8.4.21) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.4.21) + postcss-selector-not: 7.0.1(postcss@8.4.21) + postcss-value-parser: 4.2.0 + dev: true + + /postcss-pseudo-class-any-link@8.0.2(postcss@8.4.21): + resolution: {integrity: sha512-FYTIuRE07jZ2CW8POvctRgArQJ43yxhr5vLmImdKUvjFCkR09kh8pIdlCwdx/jbFm7MiW4QP58L4oOUv3grQYA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-replace-overflow-wrap@4.0.0(postcss@8.4.21): + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-selector-not@7.0.1(postcss@8.4.21): + resolution: {integrity: sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-selector-parser@6.0.11: + resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser@3.3.1: + resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + /postcss@8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.2.0 + dev: true + + /prettier-plugin-tailwindcss@0.2.3(prettier@2.8.4): + resolution: {integrity: sha512-s2N5Dh7Ao5KTV1mao5ZBnn8EKtUcDPJEkGViZIjI0Ij9TTI5zgTz4IHOxW33jOdjHKa8CSjM88scelUiC5TNRQ==} + engines: {node: '>=12.17.0'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-php': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@shufo/prettier-plugin-blade': '*' + '@trivago/prettier-plugin-sort-imports': '*' + prettier: '>=2.2.0' + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + prettier-plugin-twig-melody: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-php': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@shufo/prettier-plugin-blade': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier-plugin-twig-melody: + optional: true + dependencies: + prettier: 2.8.4 + dev: true + + /prettier@2.8.4: + resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + + /pretty-hrtime@1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + dev: true + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + /property-expr@2.0.5: + resolution: {integrity: sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==} + dev: false + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /purgecss@4.1.3: + resolution: {integrity: sha512-99cKy4s+VZoXnPxaoM23e5ABcP851nC2y2GROkkjS8eJaJtlciGavd7iYAw2V84WeBqggZ12l8ef44G99HmTaw==} + hasBin: true + dependencies: + commander: 8.3.0 + glob: 7.2.3 + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /qrcode.react@3.1.0(react@18.2.0): + resolution: {integrity: sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /react-chartjs-2@4.3.1(chart.js@3.9.1)(react@18.2.0): + resolution: {integrity: sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA==} + peerDependencies: + chart.js: ^3.5.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + chart.js: 3.9.1 + react: 18.2.0 + dev: false + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + + /react-fast-compare@2.0.4: + resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==} + dev: false + + /react-fast-compare@3.2.0: + resolution: {integrity: sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==} + dev: false + + /react-i18next@12.2.0(i18next@22.4.10)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ==} + peerDependencies: + i18next: '>= 19.0.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.21.0 + html-parse-stringify: 3.0.1 + i18next: 22.4.10 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + /react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react-router-dom@6.8.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@remix-run/router': 1.3.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.8.1(react@18.2.0) + dev: false + + /react-router@6.8.1(react@18.2.0): + resolution: {integrity: sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8' + dependencies: + '@remix-run/router': 1.3.2 + react: 18.2.0 + dev: false + + /react-select@5.7.0(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.21.0 + '@emotion/cache': 11.10.5 + '@emotion/react': 11.10.6(@types/react@18.0.28)(react@18.2.0) + '@floating-ui/dom': 1.2.1 + '@types/react-transition-group': 4.4.5 + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.28)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + dependencies: + '@babel/runtime': 7.21.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /reaptcha@1.12.1(react@18.2.0): + resolution: {integrity: sha512-zoppfGKHmo8x4PBmSrIQYQOGgVp1e8wMhr6KbwAdbQ76rSky1DcDCXLWRtBg7HGXn2hw+o+0hknafMB0rnrzZQ==} + peerDependencies: + react: ^16 || ^17 || ^18 + dependencies: + react: 18.2.0 + dev: false + + /reduce-css-calc@2.1.8: + resolution: {integrity: sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==} + dependencies: + css-unit-converter: 1.1.2 + postcss-value-parser: 3.3.1 + dev: true + + /redux-thunk@2.4.2(redux@4.2.1): + resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==} + peerDependencies: + redux: ^4 + dependencies: + redux: 4.2.1 + dev: false + + /redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + dependencies: + '@babel/runtime': 7.21.0 + dev: false + + /regenerator-runtime@0.11.1: + resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} + dev: false + + /regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + /regexp.prototype.flags@1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + /resolve@1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + /resolve@2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rgb-regex@1.0.1: + resolution: {integrity: sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==} + dev: true + + /rgba-regex@1.0.0: + resolution: {integrity: sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@3.17.2: + resolution: {integrity: sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs-compat@6.6.7: + resolution: {integrity: sha512-szN4fK+TqBPOFBcBcsR0g2cmTTUF/vaFEOZNuSdfU8/pGFnNmmn2u8SystYXG1QMrjOPBc6XTKHMVfENDf6hHw==} + dev: false + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-regex: 1.1.4 + dev: true + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + + /semver@6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver@7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + dev: true + + /sockette@2.0.6: + resolution: {integrity: sha512-W6iG8RGV6Zife3Cj+FhuyHV447E6fqFM2hKmnaQrTvg3OydINV3Msj3WPFbX76blUlUxvQSMMMdrJxce8NqI5Q==} + dev: false + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.3.2: + resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==} + dev: true + + /stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + dependencies: + internal-slot: 1.0.5 + dev: true + + /string-similarity@4.0.4: + resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.0.1 + dev: true + + /string.prototype.matchall@4.0.8: + resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + get-intrinsic: 1.2.0 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.0.1: + resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-literal@1.0.1: + resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} + dependencies: + acorn: 8.8.2 + dev: true + + /style-mod@4.0.0: + resolution: {integrity: sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==} + dev: false + + /styled-components-breakpoint@3.0.0-preview.20(@types/styled-components@5.1.26)(styled-components@5.3.6): + resolution: {integrity: sha512-rZ+Upo9lJfzK4xXRZxlvAsT90jaONa5VtoNT18fXaMLd+J75vCD1MU4/pwT/Y5Jw4rzGztMtZpelVC6P+AuNeA==} + peerDependencies: + '@types/styled-components': ^4.1.19 + styled-components: '>= 1 <= 4' + dependencies: + '@types/styled-components': 5.1.26 + styled-components: 5.3.6(react-dom@18.2.0)(react-is@17.0.2)(react@18.2.0) + dev: false + + /styled-components@5.3.6(react-dom@18.2.0)(react-is@17.0.2)(react@18.2.0): + resolution: {integrity: sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-is: '>= 16.8.0' + dependencies: + '@babel/helper-module-imports': 7.18.6 + '@babel/traverse': 7.21.2(supports-color@5.5.0) + '@emotion/is-prop-valid': 1.2.0 + '@emotion/stylis': 0.8.5 + '@emotion/unitless': 0.7.5 + babel-plugin-styled-components: 2.0.7(styled-components@5.3.6) + css-to-react-native: 3.2.0 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 17.0.2 + shallowequal: 1.1.0 + supports-color: 5.5.0 + + /stylis@4.1.3: + resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==} + dev: false + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + /swr@2.0.3(react@18.2.0): + resolution: {integrity: sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==} + engines: {pnpm: '7'} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /tabbable@6.1.1: + resolution: {integrity: sha512-4kl5w+nCB44EVRdO0g/UGoOp3vlwgycUVtkk/7DPyeLZUCuNFFKCFG6/t/DgHLrUPHjrZg6s5tNm+56Q2B0xyg==} + dev: false + + /tailwindcss@2.2.19(autoprefixer@10.4.13)(postcss@8.4.21): + resolution: {integrity: sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==} + engines: {node: '>=12.13.0'} + hasBin: true + peerDependencies: + autoprefixer: ^10.0.2 + postcss: ^8.0.9 + dependencies: + arg: 5.0.2 + autoprefixer: 10.4.13(postcss@8.4.21) + bytes: 3.1.2 + chalk: 4.1.2 + chokidar: 3.5.3 + color: 4.2.3 + cosmiconfig: 7.1.0 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.12 + fs-extra: 10.1.0 + glob-parent: 6.0.2 + html-tags: 3.2.0 + is-color-stop: 1.1.0 + is-glob: 4.0.3 + lodash: 4.17.21 + lodash.topath: 4.5.2 + modern-normalize: 1.1.0 + node-emoji: 1.11.0 + normalize-path: 3.0.0 + object-hash: 2.2.0 + postcss: 8.4.21 + postcss-js: 3.0.3 + postcss-load-config: 3.1.4(postcss@8.4.21) + postcss-nested: 5.0.6(postcss@8.4.21) + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + pretty-hrtime: 1.0.3 + purgecss: 4.1.3 + quick-lru: 5.1.1 + reduce-css-calc: 2.1.8 + resolve: 1.22.1 + tmp: 0.2.1 + transitivePeerDependencies: + - ts-node + dev: true + + /tailwindcss@3.2.7(postcss@8.4.21): + resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==} + engines: {node: '>=12.13.0'} + hasBin: true + peerDependencies: + postcss: ^8.0.9 + dependencies: + arg: 5.0.2 + chokidar: 3.5.3 + color-name: 1.1.4 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.12 + glob-parent: 6.0.2 + is-glob: 4.0.3 + lilconfig: 2.0.6 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-import: 14.1.0(postcss@8.4.21) + postcss-js: 4.0.1(postcss@8.4.21) + postcss-load-config: 3.1.4(postcss@8.4.21) + postcss-nested: 6.0.0(postcss@8.4.21) + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /timsort@0.3.0: + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + dev: true + + /tiny-case@1.0.3: + resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + dev: false + + /tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + dev: false + + /tinybench@2.3.1: + resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} + dev: true + + /tinypool@0.3.1: + resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@1.1.1: + resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==} + engines: {node: '>=14.0.0'} + dev: true + + /tmp@0.2.1: + resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} + engines: {node: '>=8.17.0'} + dependencies: + rimraf: 3.0.2 + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + dev: false + + /toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + dev: false + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + /ts-essentials@9.3.0(typescript@4.9.5): + resolution: {integrity: sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw==} + peerDependencies: + typescript: '>=4.1.0' + dependencies: + typescript: 4.9.5 + dev: true + + /ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + dev: false + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false + + /tsutils@3.21.0(typescript@4.9.5): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.9.5 + dev: true + + /twin.macro@2.8.2: + resolution: {integrity: sha512-2Vg09mp+nA70AWUedJ8WRgB2me3buq7JGbOnjHnFnNaBzomVu5k7lJ9YGpByIlre+UYr7QRhtlj7+IUKxvCrUA==} + engines: {node: '>=12.13.0'} + dependencies: + '@babel/parser': 7.21.2 + '@babel/template': 7.20.7 + autoprefixer: 10.4.13(postcss@8.4.21) + babel-plugin-macros: 2.8.0 + chalk: 4.1.2 + clean-set: 1.1.2 + color: 3.2.1 + dset: 2.1.0 + lodash.flatmap: 4.5.0 + lodash.get: 4.4.2 + lodash.merge: 4.6.2 + postcss: 8.4.21 + string-similarity: 4.0.4 + tailwindcss: 2.2.19(autoprefixer@10.4.13)(postcss@8.4.21) + timsort: 0.3.0 + transitivePeerDependencies: + - ts-node + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: false + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /ufo@1.1.0: + resolution: {integrity: sha512-LQc2s/ZDMaCN3QLpa+uzHUOQ7SdV0qgv3VBXOolQGXTaaZpIur6PwUclF5nN2hNkiTRcUugXd1zFOW3FLJ135Q==} + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /update-browserslist-db@1.0.10(browserslist@4.21.5): + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.5 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.0.28)(react@18.2.0): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + dev: false + + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite-node@0.28.5(@types/node@18.14.1): + resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==} + engines: {node: '>=v14.16.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4(supports-color@5.5.0) + mlly: 1.1.1 + pathe: 1.1.0 + picocolors: 1.0.0 + source-map: 0.6.1 + source-map-support: 0.5.21 + vite: 4.1.4(@types/node@18.14.1) + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite-plugin-full-reload@1.0.5(vite@4.1.4): + resolution: {integrity: sha512-kVZFDFWr0DxiHn6MuDVTQf7gnWIdETGlZh0hvTiMXzRN80vgF4PKbONSq8U1d0WtHsKaFODTQgJeakLacoPZEQ==} + peerDependencies: + vite: ^2 || ^3 || ^4 + dependencies: + picocolors: 1.0.0 + picomatch: 2.3.1 + vite: 4.1.4(@types/node@18.14.1) + dev: true + + /vite@4.1.4(@types/node@18.14.1): + resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.14.1 + esbuild: 0.16.17 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.17.2 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitest@0.28.5(happy-dom@8.7.2): + resolution: {integrity: sha512-pyCQ+wcAOX7mKMcBNkzDwEHRGqQvHUl0XnoHR+3Pb1hytAHISgSxv9h0gUiSiYtISXUU3rMrKiKzFYDrI6ZIHA==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.4 + '@types/chai-subset': 1.3.3 + '@types/node': 18.14.1 + '@vitest/expect': 0.28.5 + '@vitest/runner': 0.28.5 + '@vitest/spy': 0.28.5 + '@vitest/utils': 0.28.5 + acorn: 8.8.2 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + debug: 4.3.4(supports-color@5.5.0) + happy-dom: 8.7.2 + local-pkg: 0.4.3 + pathe: 1.1.0 + picocolors: 1.0.0 + source-map: 0.6.1 + std-env: 3.3.2 + strip-literal: 1.0.1 + tinybench: 2.3.1 + tinypool: 0.3.1 + tinyspy: 1.1.1 + vite: 4.1.4(@types/node@18.14.1) + vite-node: 0.28.5(@types/node@18.14.1) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + dev: false + + /w3c-keyname@2.2.6: + resolution: {integrity: sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==} + dev: false + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + + /which-typed-array@1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /word-wrap@1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /xterm-addon-fit@0.7.0(xterm@5.1.0): + resolution: {integrity: sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==} + peerDependencies: + xterm: ^5.0.0 + dependencies: + xterm: 5.1.0 + dev: false + + /xterm-addon-search-bar@0.2.0(xterm-addon-search@0.11.0)(xterm@5.1.0): + resolution: {integrity: sha512-xvXmBA/ShbnzGe5CCy0kqPNNGqjkpuaRgH3Z1iW0V71vCAPRrtJ/v/hMnysZBH7WGUYhlCQr1cJZagW2fBVvSg==} + peerDependencies: + xterm: ^4.4.0 + xterm-addon-search: ^0.5.0 + dependencies: + babel-runtime: 6.26.0 + rxjs-compat: 6.6.7 + xterm: 5.1.0 + xterm-addon-search: 0.11.0(xterm@5.1.0) + dev: false + + /xterm-addon-search@0.11.0(xterm@5.1.0): + resolution: {integrity: sha512-6U4uHXcQ7G5igsdaGqrJ9ehm7vep24bXqWxuy3AnIosXF2Z5uy2MvmYRyTGNembIqPV/x1YhBQ7uShtuqBHhOQ==} + peerDependencies: + xterm: ^5.0.0 + dependencies: + xterm: 5.1.0 + dev: false + + /xterm-addon-web-links@0.8.0(xterm@5.1.0): + resolution: {integrity: sha512-J4tKngmIu20ytX9SEJjAP3UGksah7iALqBtfTwT9ZnmFHVplCumYQsUJfKuS+JwMhjsjH61YXfndenLNvjRrEw==} + peerDependencies: + xterm: ^5.0.0 + dependencies: + xterm: 5.1.0 + dev: false + + /xterm@5.1.0: + resolution: {integrity: sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ==} + dev: false + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + + /yup@1.0.0: + resolution: {integrity: sha512-bRZIyMkoe212ahGJTE32cr2dLkJw53Va+Uw5mzsBKpcef9zCGQ23k/xtpQUfGwdWPKvCIlR8CzFwchs2rm2XpQ==} + dependencies: + property-expr: 2.0.5 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + dev: false diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 000000000..0cc56abe4 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,17 @@ +module.exports = { + plugins: [ + require('postcss-import'), + // We want to make use of nesting following the CSS Nesting spec, and not the + // SASS style nesting. + // + // @see https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting + require('tailwindcss/nesting')(require('postcss-nesting')), + require('tailwindcss'), + require('autoprefixer'), + require('postcss-preset-env')({ + features: { + 'nesting-rules': false, + }, + }), + ], +}; diff --git a/public/.gitignore b/public/.gitignore new file mode 100644 index 000000000..477f0a162 --- /dev/null +++ b/public/.gitignore @@ -0,0 +1,4 @@ +assets +assets/* +!assets/svgs +!assets/svgs/*.svg diff --git a/public/assets/svgs/not_found.svg b/public/assets/svgs/not_found.svg new file mode 100644 index 000000000..222a4152e --- /dev/null +++ b/public/assets/svgs/not_found.svg @@ -0,0 +1 @@ +not found \ No newline at end of file diff --git a/public/assets/svgs/pterodactyl.svg b/public/assets/svgs/pterodactyl.svg new file mode 100755 index 000000000..f3582adf2 --- /dev/null +++ b/public/assets/svgs/pterodactyl.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/public/assets/svgs/server_error.svg b/public/assets/svgs/server_error.svg new file mode 100644 index 000000000..726fa106d --- /dev/null +++ b/public/assets/svgs/server_error.svg @@ -0,0 +1 @@ +server down \ No newline at end of file diff --git a/public/assets/svgs/server_installing.svg b/public/assets/svgs/server_installing.svg new file mode 100644 index 000000000..d2a0ae48b --- /dev/null +++ b/public/assets/svgs/server_installing.svg @@ -0,0 +1 @@ +uploading \ No newline at end of file diff --git a/public/favicons/android-icon-144x144.png b/public/favicons/android-icon-144x144.png new file mode 100644 index 000000000..7e98c1c30 Binary files /dev/null and b/public/favicons/android-icon-144x144.png differ diff --git a/public/favicons/android-icon-192x192.png b/public/favicons/android-icon-192x192.png new file mode 100644 index 000000000..80ce4d33f Binary files /dev/null and b/public/favicons/android-icon-192x192.png differ diff --git a/public/favicons/android-icon-36x36.png b/public/favicons/android-icon-36x36.png new file mode 100644 index 000000000..729e21269 Binary files /dev/null and b/public/favicons/android-icon-36x36.png differ diff --git a/public/favicons/android-icon-48x48.png b/public/favicons/android-icon-48x48.png new file mode 100644 index 000000000..9e5fe2657 Binary files /dev/null and b/public/favicons/android-icon-48x48.png differ diff --git a/public/favicons/android-icon-72x72.png b/public/favicons/android-icon-72x72.png new file mode 100644 index 000000000..e6bb769c8 Binary files /dev/null and b/public/favicons/android-icon-72x72.png differ diff --git a/public/favicons/android-icon-96x96.png b/public/favicons/android-icon-96x96.png new file mode 100644 index 000000000..f9b8fbefb Binary files /dev/null and b/public/favicons/android-icon-96x96.png differ diff --git a/public/favicons/apple-icon-114x114.png b/public/favicons/apple-icon-114x114.png new file mode 100644 index 000000000..3920011b1 Binary files /dev/null and b/public/favicons/apple-icon-114x114.png differ diff --git a/public/favicons/apple-icon-120x120.png b/public/favicons/apple-icon-120x120.png new file mode 100644 index 000000000..81b72ba81 Binary files /dev/null and b/public/favicons/apple-icon-120x120.png differ diff --git a/public/favicons/apple-icon-144x144.png b/public/favicons/apple-icon-144x144.png new file mode 100644 index 000000000..7e98c1c30 Binary files /dev/null and b/public/favicons/apple-icon-144x144.png differ diff --git a/public/favicons/apple-icon-152x152.png b/public/favicons/apple-icon-152x152.png new file mode 100644 index 000000000..6623bd585 Binary files /dev/null and b/public/favicons/apple-icon-152x152.png differ diff --git a/public/favicons/apple-icon-180x180.png b/public/favicons/apple-icon-180x180.png new file mode 100644 index 000000000..0503d522b Binary files /dev/null and b/public/favicons/apple-icon-180x180.png differ diff --git a/public/favicons/apple-icon-57x57.png b/public/favicons/apple-icon-57x57.png new file mode 100644 index 000000000..466878ee1 Binary files /dev/null and b/public/favicons/apple-icon-57x57.png differ diff --git a/public/favicons/apple-icon-60x60.png b/public/favicons/apple-icon-60x60.png new file mode 100644 index 000000000..09a79d997 Binary files /dev/null and b/public/favicons/apple-icon-60x60.png differ diff --git a/public/favicons/apple-icon-72x72.png b/public/favicons/apple-icon-72x72.png new file mode 100644 index 000000000..e6bb769c8 Binary files /dev/null and b/public/favicons/apple-icon-72x72.png differ diff --git a/public/favicons/apple-icon-76x76.png b/public/favicons/apple-icon-76x76.png new file mode 100644 index 000000000..f656479b8 Binary files /dev/null and b/public/favicons/apple-icon-76x76.png differ diff --git a/public/favicons/apple-icon-precomposed.png b/public/favicons/apple-icon-precomposed.png new file mode 100644 index 000000000..277e51bf0 Binary files /dev/null and b/public/favicons/apple-icon-precomposed.png differ diff --git a/public/favicons/apple-icon.png b/public/favicons/apple-icon.png new file mode 100644 index 000000000..277e51bf0 Binary files /dev/null and b/public/favicons/apple-icon.png differ diff --git a/public/favicons/browserconfig.xml b/public/favicons/browserconfig.xml index e3cb776ee..c55414822 100644 --- a/public/favicons/browserconfig.xml +++ b/public/favicons/browserconfig.xml @@ -1,9 +1,2 @@ - - - - - #165ed4 - - - +#ffffff \ No newline at end of file diff --git a/public/favicons/favicon-16x16.png b/public/favicons/favicon-16x16.png index d568bd20a..9597c6f36 100644 Binary files a/public/favicons/favicon-16x16.png and b/public/favicons/favicon-16x16.png differ diff --git a/public/favicons/favicon-32x32.png b/public/favicons/favicon-32x32.png index edfd13405..737917e1a 100644 Binary files a/public/favicons/favicon-32x32.png and b/public/favicons/favicon-32x32.png differ diff --git a/public/favicons/favicon-96x96.png b/public/favicons/favicon-96x96.png new file mode 100644 index 000000000..f9b8fbefb Binary files /dev/null and b/public/favicons/favicon-96x96.png differ diff --git a/public/favicons/favicon.ico b/public/favicons/favicon.ico index 99e5bffe2..bc2228831 100644 Binary files a/public/favicons/favicon.ico and b/public/favicons/favicon.ico differ diff --git a/public/favicons/manifest.json b/public/favicons/manifest.json index 796d1ff0c..013d4a6a5 100644 --- a/public/favicons/manifest.json +++ b/public/favicons/manifest.json @@ -1,18 +1,41 @@ { - "name": "", - "icons": [ - { - "src": "favicons/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "favicons/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] } \ No newline at end of file diff --git a/public/favicons/ms-icon-144x144.png b/public/favicons/ms-icon-144x144.png new file mode 100644 index 000000000..7e98c1c30 Binary files /dev/null and b/public/favicons/ms-icon-144x144.png differ diff --git a/public/favicons/ms-icon-150x150.png b/public/favicons/ms-icon-150x150.png new file mode 100644 index 000000000..cdd392c86 Binary files /dev/null and b/public/favicons/ms-icon-150x150.png differ diff --git a/public/favicons/ms-icon-310x310.png b/public/favicons/ms-icon-310x310.png new file mode 100644 index 000000000..7b84fc129 Binary files /dev/null and b/public/favicons/ms-icon-310x310.png differ diff --git a/public/favicons/ms-icon-70x70.png b/public/favicons/ms-icon-70x70.png new file mode 100644 index 000000000..383bb7675 Binary files /dev/null and b/public/favicons/ms-icon-70x70.png differ diff --git a/public/index.php b/public/index.php index 233cba743..796d29022 100644 --- a/public/index.php +++ b/public/index.php @@ -7,6 +7,21 @@ */ define('LARAVEL_START', microtime(true)); +/* +|-------------------------------------------------------------------------- +| Check If Application Is Under Maintenance +|-------------------------------------------------------------------------- +| +| If the application is maintenance / demo mode via the "down" command we +| will require this file so that any pre-rendered template can be shown +| instead of starting the framework, which could cause an exception. +| +*/ + +if (file_exists(__DIR__ . '/../storage/framework/maintenance.php')) { + require __DIR__ . '/../storage/framework/maintenance.php'; +} + /* |-------------------------------------------------------------------------- | Register The Auto Loader diff --git a/public/js/autocomplete.js b/public/js/autocomplete.js deleted file mode 100644 index 15c43797e..000000000 --- a/public/js/autocomplete.js +++ /dev/null @@ -1,6 +0,0 @@ -// Hacky fix for browsers ignoring autocomplete="off" -$(document).ready(function() { - $('.form-autocomplete-stop').on('click', function () { - $(this).removeAttr('readonly').blur().focus(); - }); -}); diff --git a/public/js/keyboard.polyfill.js b/public/js/keyboard.polyfill.js deleted file mode 100644 index cc78b36da..000000000 --- a/public/js/keyboard.polyfill.js +++ /dev/null @@ -1,121 +0,0 @@ -/* global define, KeyboardEvent, module */ - -(function () { - - var keyboardeventKeyPolyfill = { - polyfill: polyfill, - keys: { - 3: 'Cancel', - 6: 'Help', - 8: 'Backspace', - 9: 'Tab', - 12: 'Clear', - 13: 'Enter', - 16: 'Shift', - 17: 'Control', - 18: 'Alt', - 19: 'Pause', - 20: 'CapsLock', - 27: 'Escape', - 28: 'Convert', - 29: 'NonConvert', - 30: 'Accept', - 31: 'ModeChange', - 32: ' ', - 33: 'PageUp', - 34: 'PageDown', - 35: 'End', - 36: 'Home', - 37: 'ArrowLeft', - 38: 'ArrowUp', - 39: 'ArrowRight', - 40: 'ArrowDown', - 41: 'Select', - 42: 'Print', - 43: 'Execute', - 44: 'PrintScreen', - 45: 'Insert', - 46: 'Delete', - 48: ['0', ')'], - 49: ['1', '!'], - 50: ['2', '@'], - 51: ['3', '#'], - 52: ['4', '$'], - 53: ['5', '%'], - 54: ['6', '^'], - 55: ['7', '&'], - 56: ['8', '*'], - 57: ['9', '('], - 91: 'OS', - 93: 'ContextMenu', - 144: 'NumLock', - 145: 'ScrollLock', - 181: 'VolumeMute', - 182: 'VolumeDown', - 183: 'VolumeUp', - 186: [';', ':'], - 187: ['=', '+'], - 188: [',', '<'], - 189: ['-', '_'], - 190: ['.', '>'], - 191: ['/', '?'], - 192: ['`', '~'], - 219: ['[', '{'], - 220: ['\\', '|'], - 221: [']', '}'], - 222: ["'", '"'], - 224: 'Meta', - 225: 'AltGraph', - 246: 'Attn', - 247: 'CrSel', - 248: 'ExSel', - 249: 'EraseEof', - 250: 'Play', - 251: 'ZoomOut' - } - }; - - // Function keys (F1-24). - var i; - for (i = 1; i < 25; i++) { - keyboardeventKeyPolyfill.keys[111 + i] = 'F' + i; - } - - // Printable ASCII characters. - var letter = ''; - for (i = 65; i < 91; i++) { - letter = String.fromCharCode(i); - keyboardeventKeyPolyfill.keys[i] = [letter.toLowerCase(), letter.toUpperCase()]; - } - - function polyfill () { - if (!('KeyboardEvent' in window) || - 'key' in KeyboardEvent.prototype) { - return false; - } - - // Polyfill `key` on `KeyboardEvent`. - var proto = { - get: function (x) { - var key = keyboardeventKeyPolyfill.keys[this.which || this.keyCode]; - - if (Array.isArray(key)) { - key = key[+this.shiftKey]; - } - - return key; - } - }; - Object.defineProperty(KeyboardEvent.prototype, 'key', proto); - return proto; - } - - if (typeof define === 'function' && define.amd) { - define('keyboardevent-key-polyfill', keyboardeventKeyPolyfill); - } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { - module.exports = keyboardeventKeyPolyfill; - } else if (window) { - window.keyboardeventKeyPolyfill = keyboardeventKeyPolyfill; - } - -})(); diff --git a/public/js/laroute.js b/public/js/laroute.js deleted file mode 100644 index ca120c0d9..000000000 --- a/public/js/laroute.js +++ /dev/null @@ -1,195 +0,0 @@ -(function () { - - var laroute = (function () { - - var routes = { - - absolute: false, - rootUrl: 'http://pterodactyl.local', - routes : [{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"},{"host":null,"methods":["DELETE"],"uri":"_debugbar\/cache\/{key}\/{tags?}","name":"debugbar.cache.delete","action":"Barryvdh\Debugbar\Controllers\CacheController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"status\/{server}","name":"index.status","action":"Pterodactyl\Http\Controllers\Base\IndexController@status"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\ClientApiController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\ClientApiController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\ClientApiController@store"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{identifier}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\ClientApiController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":"account.security.totp.set","action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":"account.security.totp.disable","action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/statistics","name":"admin.statistics","action":"Pterodactyl\Http\Controllers\Admin\StatisticsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/api","name":"admin.api.index","action":"Pterodactyl\Http\Controllers\Admin\ApiController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/api\/new","name":"admin.api.new","action":"Pterodactyl\Http\Controllers\Admin\ApiController@create"},{"host":null,"methods":["POST"],"uri":"admin\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ApiController@store"},{"host":null,"methods":["DELETE"],"uri":"admin\/api\/revoke\/{identifier}","name":"admin.api.delete","action":"Pterodactyl\Http\Controllers\Admin\ApiController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations\/view\/{location}","name":"admin.locations.view","action":"Pterodactyl\Http\Controllers\Admin\LocationController@view"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/view\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/view\/{host}","name":"admin.databases.view","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"admin\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\Settings\IndexController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/mail","name":"admin.settings.mail","action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/mail\/test","name":"admin.settings.mail.test","action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@test"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/advanced","name":"admin.settings.advanced","action":"Pterodactyl\Http\Controllers\Admin\Settings\AdvancedController@index"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\IndexController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings\/mail","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings\/advanced","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\AdvancedController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@json"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{user}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@store"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/reinstall","name":"admin.servers.view.manage.reinstall","action":"Pterodactyl\Http\Controllers\Admin\ServersController@reinstallServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{server}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["PATCH"],"uri":"admin\/nodes\/view\/{node}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocations","name":"admin.nodes.view.allocation.removeMultiple","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveMultiple"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests","name":"admin.nests","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/new","name":"admin.nests.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/view\/{nest}","name":"admin.nests.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/new","name":"admin.nests.egg.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}","name":"admin.nests.egg.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/export","name":"admin.nests.egg.export","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":"admin.nests.egg.variables","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":"admin.nests.egg.scripts","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@index"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/import","name":"admin.nests.egg.import","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@import"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@store"},{"host":null,"methods":["PUT"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":"admin.nests.egg.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs","name":"admin.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new","name":"admin.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new\/template","name":"admin.packs.new.template","action":"Pterodactyl\Http\Controllers\Admin\PackController@newTemplate"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/view\/{pack}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@store"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{pack}\/export\/{files?}","name":"admin.packs.view.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["PATCH"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@loginUsingTotp"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset\/{token}","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/console","name":"server.console","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@console"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/name","name":"server.settings.name","action":"Pterodactyl\Http\Controllers\Server\Settings\NameController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\Settings\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@update"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/name","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\NameController@update"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/databases","name":"server.databases.index","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/databases\/new","name":"server.databases.new","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@store"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/databases\/password","name":"server.databases.password","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/databases\/delete\/{database}","name":"server.databases.delete","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\DownloadController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@directory"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules","name":"server.schedules","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/new","name":"server.schedules.new","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":"server.schedules.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@update"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/toggle","name":"server.schedules.toggle","action":"Pterodactyl\Http\Controllers\Server\Tasks\ActionController@toggle"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/trigger","name":"server.schedules.trigger","action":"Pterodactyl\Http\Controllers\Server\Tasks\ActionController@trigger"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/users","name":"api.application.users","action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/users\/{user}","name":"api.application.users.view","action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/users\/external\/{external_id}","name":"api.application.users.external","action":"Pterodactyl\Http\Controllers\Api\Application\Users\ExternalUserController@index"},{"host":null,"methods":["POST"],"uri":"api\/application\/users","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/users\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/users\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Users\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nodes","name":"api.application.nodes","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nodes\/{node}","name":"api.application.nodes.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@view"},{"host":null,"methods":["POST"],"uri":"api\/application\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@store"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/nodes\/{node}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/nodes\/{node}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\NodeController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nodes\/{node}\/allocations","name":"api.application.allocations","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\AllocationController@index"},{"host":null,"methods":["POST"],"uri":"api\/application\/nodes\/{node}\/allocations","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\AllocationController@store"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/nodes\/{node}\/allocations\/{allocation}","name":"api.application.allocations.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nodes\AllocationController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/locations","name":"api.applications.locations","action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/locations\/{location}","name":"api.application.locations.view","action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@view"},{"host":null,"methods":["POST"],"uri":"api\/application\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@store"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/locations\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/locations\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Locations\LocationController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers","name":"api.application.servers","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/{server}","name":"api.application.servers.view","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/external\/{external_id}","name":"api.application.servers.external","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ExternalServerController@index"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/servers\/{server}\/details","name":"api.application.servers.details","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerDetailsController@details"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/servers\/{server}\/build","name":"api.application.servers.build","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerDetailsController@build"},{"host":null,"methods":["PATCH"],"uri":"api\/application\/servers\/{server}\/startup","name":"api.application.servers.startup","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\StartupController@index"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@store"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/suspend","name":"api.application.servers.suspend","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@suspend"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/unsuspend","name":"api.application.servers.unsuspend","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@unsuspend"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/reinstall","name":"api.application.servers.reinstall","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@reinstall"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/rebuild","name":"api.application.servers.rebuild","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerManagementController@rebuild"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/servers\/{server}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@delete"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/servers\/{server}\/{force?}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\ServerController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/{server}\/databases","name":"api.application.servers.databases","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/servers\/{server}\/databases\/{database}","name":"api.application.servers.databases.view","action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@store"},{"host":null,"methods":["POST"],"uri":"api\/application\/servers\/{server}\/databases\/{database}\/reset-password","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@resetPassword"},{"host":null,"methods":["DELETE"],"uri":"api\/application\/servers\/{server}\/databases\/{database}","name":null,"action":"Pterodactyl\Http\Controllers\Api\Application\Servers\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests","name":"api.application.nests","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\NestController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests\/{nest}","name":"api.application.nests.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\NestController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests\/{nest}\/eggs","name":"api.application.nests.eggs","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\EggController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/application\/nests\/{nest}\/eggs\/{egg}","name":"api.application.nests.eggs.view","action":"Pterodactyl\Http\Controllers\Api\Application\Nests\EggController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/client","name":"api.client.index","action":"Pterodactyl\Http\Controllers\Api\Client\ClientController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/client\/servers\/{server}","name":"api.client.servers.view","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/client\/servers\/{server}\/utilization","name":"api.client.servers.resources","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\ResourceUtilizationController@index"},{"host":null,"methods":["POST"],"uri":"api\/client\/servers\/{server}\/command","name":"api.client.servers.command","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\CommandController@index"},{"host":null,"methods":["POST"],"uri":"api\/client\/servers\/{server}\/power","name":"api.client.servers.power","action":"Pterodactyl\Http\Controllers\Api\Client\Servers\PowerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/authenticate\/{token}","name":"api.remote.authenticate","action":"Pterodactyl\Http\Controllers\Api\Remote\ValidateKeyController@index"},{"host":null,"methods":["POST"],"uri":"api\/remote\/download-file","name":"api.remote.download_file","action":"Pterodactyl\Http\Controllers\Api\Remote\FileDownloadController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs","name":"api.remote.eggs","action":"Pterodactyl\Http\Controllers\Api\Remote\EggRetrievalController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs\/{uuid}","name":"api.remote.eggs.download","action":"Pterodactyl\Http\Controllers\Api\Remote\EggRetrievalController@download"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/scripts\/{uuid}","name":"api.remote.scripts","action":"Pterodactyl\Http\Controllers\Api\Remote\EggInstallController@index"},{"host":null,"methods":["POST"],"uri":"api\/remote\/sftp","name":"api.remote.sftp","action":"Pterodactyl\Http\Controllers\Api\Remote\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/configure\/{token}","name":"daemon.configuration","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@configuration"},{"host":null,"methods":["POST"],"uri":"daemon\/install","name":"daemon.install","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@markInstall"}], - prefix: '', - - route : function (name, parameters, route) { - route = route || this.getByName(name); - - if ( ! route ) { - return undefined; - } - - return this.toRoute(route, parameters); - }, - - url: function (url, parameters) { - parameters = parameters || []; - - var uri = url + '/' + parameters.join('/'); - - return this.getCorrectUrl(uri); - }, - - toRoute : function (route, parameters) { - var uri = this.replaceNamedParameters(route.uri, parameters); - var qs = this.getRouteQueryString(parameters); - - if (this.absolute && this.isOtherHost(route)){ - return "//" + route.host + "/" + uri + qs; - } - - return this.getCorrectUrl(uri + qs); - }, - - isOtherHost: function (route){ - return route.host && route.host != window.location.hostname; - }, - - replaceNamedParameters : function (uri, parameters) { - uri = uri.replace(/\{(.*?)\??\}/g, function(match, key) { - if (parameters.hasOwnProperty(key)) { - var value = parameters[key]; - delete parameters[key]; - return value; - } else { - return match; - } - }); - - // Strip out any optional parameters that were not given - uri = uri.replace(/\/\{.*?\?\}/g, ''); - - return uri; - }, - - getRouteQueryString : function (parameters) { - var qs = []; - for (var key in parameters) { - if (parameters.hasOwnProperty(key)) { - qs.push(key + '=' + parameters[key]); - } - } - - if (qs.length < 1) { - return ''; - } - - return '?' + qs.join('&'); - }, - - getByName : function (name) { - for (var key in this.routes) { - if (this.routes.hasOwnProperty(key) && this.routes[key].name === name) { - return this.routes[key]; - } - } - }, - - getByAction : function(action) { - for (var key in this.routes) { - if (this.routes.hasOwnProperty(key) && this.routes[key].action === action) { - return this.routes[key]; - } - } - }, - - getCorrectUrl: function (uri) { - var url = this.prefix + '/' + uri.replace(/^\/?/, ''); - - if ( ! this.absolute) { - return url; - } - - return this.rootUrl.replace('/\/?$/', '') + url; - } - }; - - var getLinkAttributes = function(attributes) { - if ( ! attributes) { - return ''; - } - - var attrs = []; - for (var key in attributes) { - if (attributes.hasOwnProperty(key)) { - attrs.push(key + '="' + attributes[key] + '"'); - } - } - - return attrs.join(' '); - }; - - var getHtmlLink = function (url, title, attributes) { - title = title || url; - attributes = getLinkAttributes(attributes); - - return '' + title + ''; - }; - - return { - // Generate a url for a given controller action. - // Router.action('HomeController@getIndex', [params = {}]) - action : function (name, parameters) { - parameters = parameters || {}; - - return routes.route(name, parameters, routes.getByAction(name)); - }, - - // Generate a url for a given named route. - // Router.route('routeName', [params = {}]) - route : function (route, parameters) { - parameters = parameters || {}; - - return routes.route(route, parameters); - }, - - // Generate a fully qualified URL to the given path. - // Router.route('url', [params = {}]) - url : function (route, parameters) { - parameters = parameters || {}; - - return routes.url(route, parameters); - }, - - // Generate a html link to the given url. - // Router.link_to('foo/bar', [title = url], [attributes = {}]) - link_to : function (url, title, attributes) { - url = this.url(url); - - return getHtmlLink(url, title, attributes); - }, - - // Generate a html link to the given route. - // Router.link_to_route('route.name', [title=url], [parameters = {}], [attributes = {}]) - link_to_route : function (route, title, parameters, attributes) { - var url = this.route(route, parameters); - - return getHtmlLink(url, title, attributes); - }, - - // Generate a html link to the given controller action. - // Router.link_to_action('HomeController@getIndex', [title=url], [parameters = {}], [attributes = {}]) - link_to_action : function(action, title, parameters, attributes) { - var url = this.action(action, parameters); - - return getHtmlLink(url, title, attributes); - } - - }; - - }).call(this); - - /** - * Expose the class either via AMD, CommonJS or the global object - */ - if (typeof define === 'function' && define.amd) { - define(function () { - return laroute; - }); - } - else if (typeof module === 'object' && module.exports){ - module.exports = laroute; - } - else { - window.Router = laroute; - } - -}).call(this); - diff --git a/public/robots.txt b/public/robots.txt index eb0536286..1f53798bb 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,2 @@ User-agent: * -Disallow: +Disallow: / diff --git a/public/themes/pterodactyl/css/checkbox.css b/public/themes/pterodactyl/css/checkbox.css deleted file mode 100644 index a75e63af6..000000000 --- a/public/themes/pterodactyl/css/checkbox.css +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Bootsnip - "Bootstrap Checkboxes/Radios" - * Bootstrap 3.2.0 Snippet by i-heart-php - * - * Copyright (c) 2013 Bootsnipp.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. - */ - .checkbox { - padding-left: 20px; -} -.checkbox label { - display: inline-block; - position: relative; - padding-left: 5px; -} -.checkbox label::before { - content: ""; - display: inline-block; - position: absolute; - width: 17px; - height: 17px; - left: 0; - top: 2.5px; - margin-left: -20px; - border: 1px solid #cccccc; - border-radius: 3px; - background-color: #fff; - -webkit-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; - -o-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; - transition: border 0.15s ease-in-out, color 0.15s ease-in-out; -} -.checkbox label::after { - display: inline-block; - position: absolute; - width: 16px; - height: 16px; - left: 0; - top: 2.5px; - margin-left: -20px; - padding-left: 3px; - padding-top: 1px; - font-size: 11px; - color: #555555; -} -.checkbox input[type="checkbox"] { - opacity: 0; -} -.checkbox input[type="checkbox"]:focus + label::before { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.checkbox input[type="checkbox"]:checked + label::after { - font-family: 'FontAwesome'; - content: "\f00c"; -} -.checkbox input[type="checkbox"]:disabled + label { - opacity: 0.65; -} -.checkbox input[type="checkbox"]:disabled + label::before { - background-color: #eeeeee; - cursor: not-allowed; -} -.checkbox.checkbox-circle label::before { - border-radius: 50%; -} -.checkbox.checkbox-inline { - margin-top: 0; -} -.checkbox-primary input[type="checkbox"]:checked + label::before { - background-color: #428bca; - border-color: #428bca; -} -.checkbox-primary input[type="checkbox"]:checked + label::after { - color: #fff; -} -.checkbox-danger input[type="checkbox"]:checked + label::before { - background-color: #d9534f; - border-color: #d9534f; -} -.checkbox-danger input[type="checkbox"]:checked + label::after { - color: #fff; -} -.checkbox-info input[type="checkbox"]:checked + label::before { - background-color: #5bc0de; - border-color: #5bc0de; -} -.checkbox-info input[type="checkbox"]:checked + label::after { - color: #fff; -} -.checkbox-warning input[type="checkbox"]:checked + label::before { - background-color: #f0ad4e; - border-color: #f0ad4e; -} -.checkbox-warning input[type="checkbox"]:checked + label::after { - color: #fff; -} -.checkbox-success input[type="checkbox"]:checked + label::before { - background-color: #5cb85c; - border-color: #5cb85c; -} -.checkbox-success input[type="checkbox"]:checked + label::after { - color: #fff; -} -.radio { - padding-left: 20px; -} -.radio label { - display: inline-block; - position: relative; - padding-left: 5px; -} -.radio label::before { - content: ""; - display: inline-block; - position: absolute; - width: 17px; - height: 17px; - left: 0; - margin-left: -20px; - border: 1px solid #cccccc; - border-radius: 50%; - background-color: #fff; - -webkit-transition: border 0.15s ease-in-out; - -o-transition: border 0.15s ease-in-out; - transition: border 0.15s ease-in-out; -} -.radio label::after { - display: inline-block; - position: absolute; - content: " "; - width: 11px; - height: 11px; - left: 3px; - top: 3px; - margin-left: -20px; - border-radius: 50%; - background-color: #555555; - -webkit-transform: scale(0, 0); - -ms-transform: scale(0, 0); - -o-transform: scale(0, 0); - transform: scale(0, 0); - -webkit-transition: -webkit-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); - -moz-transition: -moz-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); - -o-transition: -o-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); - transition: transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); -} -.radio input[type="radio"] { - opacity: 0; -} -.radio input[type="radio"]:focus + label::before { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.radio input[type="radio"]:checked + label::after { - -webkit-transform: scale(1, 1); - -ms-transform: scale(1, 1); - -o-transform: scale(1, 1); - transform: scale(1, 1); -} -.radio input[type="radio"]:disabled + label { - opacity: 0.65; -} -.radio input[type="radio"]:disabled + label::before { - cursor: not-allowed; -} -.radio.radio-inline { - margin-top: 0; -} -.radio-primary input[type="radio"] + label::after { - background-color: #428bca; -} -.radio-primary input[type="radio"]:checked + label::before { - border-color: #428bca; -} -.radio-primary input[type="radio"]:checked + label::after { - background-color: #428bca; -} -.radio-danger input[type="radio"] + label::after { - background-color: #d9534f; -} -.radio-danger input[type="radio"]:checked + label::before { - border-color: #d9534f; -} -.radio-danger input[type="radio"]:checked + label::after { - background-color: #d9534f; -} -.radio-info input[type="radio"] + label::after { - background-color: #5bc0de; -} -.radio-info input[type="radio"]:checked + label::before { - border-color: #5bc0de; -} -.radio-info input[type="radio"]:checked + label::after { - background-color: #5bc0de; -} -.radio-warning input[type="radio"] + label::after { - background-color: #f0ad4e; -} -.radio-warning input[type="radio"]:checked + label::before { - border-color: #f0ad4e; -} -.radio-warning input[type="radio"]:checked + label::after { - background-color: #f0ad4e; -} -.radio-success input[type="radio"] + label::after { - background-color: #5cb85c; -} -.radio-success input[type="radio"]:checked + label::before { - border-color: #5cb85c; -} -.radio-success input[type="radio"]:checked + label::after { - background-color: #5cb85c; -} diff --git a/public/themes/pterodactyl/css/pterodactyl.css b/public/themes/pterodactyl/css/pterodactyl.css deleted file mode 100644 index a4e4c4cf7..000000000 --- a/public/themes/pterodactyl/css/pterodactyl.css +++ /dev/null @@ -1,484 +0,0 @@ -/** - * Pterodactyl - Panel - * Copyright (c) 2015 - 2017 Dane Everitt - * - * 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. - */ - @import 'checkbox.css'; - -.login-page { - background: #10529f; -} - -#login-position-elements { - margin: 25% auto; -} - -.login-logo { - color: #fff; - font-weight: 400; -} - -.login-copyright { - color: rgba(255, 255, 255, 0.3); -} - -.login-copyright > a { - color: rgba(255, 255, 255, 0.6); -} - -.particles-js-canvas-el { - position: absolute; - width: 100%; - height: 100%; - top: 0; - z-index: -1; -} - -.pterodactyl-login-box { - background: rgba(0, 0, 0, 0.25); - border-radius: 3px; - padding: 20px; -} - -.pterodactyl-login-input > input { - background: rgba(0, 0, 0, 0.4); - border: 1px solid #000; - border-radius: 2px; - color: #fff; -} - -.pterodactyl-login-input > .form-control-feedback { - color: #fff; -} - -.pterodactyl-login-button--main { - background: rgba(0, 0, 0, 0.4); - border: 1px solid #000; - border-radius: 2px; - color: #fff; -} - -.pterodactyl-login-button--main:hover { - background: rgba(0, 0, 0, 0.7); - border: 1px solid #000; - border-radius: 2px; - color: #fff; -} - -.pterodactyl-login-button--left { - background: rgba(255, 255, 255, 0.4); - border: 1px solid rgba(255, 255, 255, 0.6); - border-radius: 2px; - color: #fff; -} - -.pterodactyl-login-button--left:hover { - background: rgba(255, 255, 255, 0.6); - border: 1px solid rgba(255, 255, 255, 0.8); - border-radius: 2px; - color: #fff; -} - -.weight-100 { - font-weight: 100; -} - -.weight-300 { - font-weight: 300; -} - -.btn-clear { - background: transparent; -} - -.user-panel > .info { - position: relative; - left: 0; -} - -code { - background-color: #eef1f6; - color: #596981; - border-radius: 2px; - padding-left: 4px; - padding-right: 4px; - line-height: 1.4; - font-size: 85%; - border: 1px solid rgba(0, 0, 0, .1); - display: inline-block; -} - -p { - line-height: 1.6 !important; -} - -p.small { - margin-top: 3px !important; -} - -.control-sidebar-dark .control-sidebar-menu > li > a.active { - background: #1e282c; -} - -.callout-nomargin { - margin: 0; -} - -.table { - font-size: 14px !important; -} - -.table .min-size { - width:1px; - white-space: nowrap; -} - -@media (max-width:767px) { - .box-header > .box-tools { - position: relative !important; - padding: 0px 10px 10px; - } -} - -.middle, .align-middle { - vertical-align: middle !important; -} - -#fileOptionMenu.dropdown-menu > li > a { - padding:3px 6px; -} - -.hasFileHover { - border: 2px dashed #0087F7; - border-top: 0 !important; - border-radius: 5px; - margin: 0; - opacity: 0.5; -} - -.hasFileHover * { - pointer-events: none !important; -} - -td.has-progress { - padding: 0px !important; - border-top: 0px !important; -} - -.progress.progress-table-bottom { - margin: 0 !important; - height:5px !important; - padding:0; - border:0; -} - -.muted { - filter: alpha(opacity=20); - opacity: 0.2; -} - -.muted-hover:hover { - filter: alpha(opacity=100); - opacity: 1; -} - -.use-pointer { - cursor: pointer !important; -} - -.input-loader { - display: none; - position:relative; - top: -25px; - float: right; - right: 5px; - color: #cccccc; - height: 0; -} - -.box-header > .form-group { - margin-bottom: 0; -} - -.box-header > .form-group > div > p.small { - margin: 0; -} - -.no-margin { - margin: 0 !important; -} - -li.select2-results__option--highlighted[aria-selected="false"] > .user-block > .username > a { - color: #fff; -} - -li.select2-results__option--highlighted[aria-selected="false"] > .user-block > .description { - color: #eee; -} - -.select2-selection.select2-selection--multiple { - min-height: 36px !important; -} - -.select2-search--inline .select2-search__field:focus { - outline: none; - border: 0 !important; -} - -.img-bordered-xs { - border: 1px solid #d2d6de; - padding: 1px; -} - -span[aria-labelledby="select2-pUserId-container"] { - padding-left: 2px !important; -} - -.box { - box-shadow: 0 0 0 1px rgba(89, 105, 128, .1), 0 1px 3px 0 rgba(89, 105, 128, .1), 0 1px 2px 0 rgba(0, 0, 0, .05) !important; -} - -.alert-danger { - color: #ffffff !important; - background: #d64242 !important; - border: 1px solid #841d1d; -} - -.alert-info { - color: #ffffff !important; - background: #408fec !important; - border: 1px solid #1055a5; -} - -.alert-success { - color: #ffffff !important; - background: #51b060 !important; - border: 1px solid #2b5f33; -} - -.alert-warning { - color: #ffffff !important; - background: #fa9636 !important; - border: 1px solid #b45b05; -} - -.callout-slim a { - color: #555 !important; -} - -.bg-purple { - background-color: #79589f !important; -} - -.label-default { - background-color: #eef1f6 !important; -} - -.callout.callout-info.callout-slim { - border: 1px solid #1055a5 !important; - border-left: 5px solid #1055a5 !important; - border-right: 5px solid #1055a5 !important; - color: #777 !important; - background: transparent !important; -} - -.callout.callout-danger.callout-slim { - border: 1px solid #c23321 !important; - border-left: 5px solid #c23321 !important; - border-right: 5px solid #c23321 !important; - color: #777 !important; - background: transparent !important; -} - -.callout.callout-warning.callout-slim { - border: 1px solid #c87f0a !important; - border-left: 5px solid #c87f0a !important; - border-right: 5px solid #c87f0a !important; - color: #777 !important; - background: transparent !important; -} - -.callout.callout-success.callout-slim { - border: 1px solid #00733e !important; - border-left: 5px solid #00733e !important; - border-right: 5px solid #00733e !important; - color: #777 !important; - background: transparent !important; -} - -.callout.callout-default.callout-slim { - border: 1px solid #eee !important; - border-left: 5px solid #eee !important; - border-right: 5px solid #eee !important; - color: #777 !important; - background: transparent !important; -} - -.tab-pane .box-footer { - margin: 0 -10px -10px; -} - -.select2-container{ width: 100% !important; } - -.nav-tabs-custom > .nav-tabs > li:hover { - border-top-color:#3c8dbc; -} - -.nav-tabs-custom > .nav-tabs > li.active.tab-danger, .nav-tabs-custom > .nav-tabs > li.tab-danger:hover { - border-top-color: #c23321; -} - -.nav-tabs-custom > .nav-tabs > li.active.tab-success, .nav-tabs-custom > .nav-tabs > li.tab-success:hover { - border-top-color: #00733e; -} - -.nav-tabs-custom > .nav-tabs > li.active.tab-info, .nav-tabs-custom > .nav-tabs > li.tab-info:hover { - border-top-color: #0097bc; -} - -.nav-tabs-custom > .nav-tabs > li.active.tab-warning, .nav-tabs-custom > .nav-tabs > li.tab-warning:hover { - border-top-color: #c87f0a; -} - -.nav-tabs-custom.nav-tabs-floating > .nav-tabs { - border-bottom: 0px !important; -} - -.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li { - margin-bottom: 0px !important; -} - -.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active, -.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child:hover { - border-radius: 3px 0 0 0; -} - -.nav-tabs-custom.nav-tabs-floating > .nav-tabs > li:first-child.active > a { - border-radius: 0 0 0 3px; -} - -.position-relative { - position: relative; -} - -.no-pad-bottom { - padding-bottom: 0 !important; -} - -.no-margin-bottom { - margin-bottom: 0 !important; -} - -.btn-icon > i.fa { - line-height: 1.5; -} - -.btn.active, .btn.active.focus { - background-color: #408fec; -} - -.strong { - font-weight: bold !important; -} - -.server-description > td { - padding-top: 0 !important; - border-top: 0 !important; -} - -tr:hover + tr.server-description { - background-color: #f5f5f5 !important; -} - -.login-corner-info { - position: absolute; - bottom: 5px; - right: 10px; - color: white; -} - -input.form-autocomplete-stop[readonly] { - background: inherit; - cursor: text; -} - -/* fix Google Recaptcha badge */ -.grecaptcha-badge { - bottom: 54px !important; - background: white; - box-shadow: none !important; -} - -.dropdown-massactions { - min-width: 80px; -} - -.select-all-files { - position: relative; - bottom: 1px; - margin-right: 7px !important; -} - -.select-file { - position: relative; - bottom: 1px; - margin-right: 2px !important; -} - -.select-folder { - position: relative; - bottom: 1px; - margin-right: 5px !important; -} - -label.control-label > span { - font-size: 80%; - font-weight: 400; - font-style: italic; - color: #dd4b39; -} - -label.control-label > span.field-required:before { - content: "required"; - color: #dd4b39; -} - -label.control-label > span.field-optional:before { - content: "optional"; - color: #bbbbbb; -} - -.pagination > li > a, .pagination > li > span { - padding: 3px 10px !important; -} - -.logo-mini > img { - height: 42px; - width: auto; -} - -.search01 { - width: 30%; -} - -.number-info-box-content { - padding: 15px 10px 0; -} - diff --git a/public/themes/pterodactyl/css/terminal.css b/public/themes/pterodactyl/css/terminal.css deleted file mode 100644 index a9bd3db1b..000000000 --- a/public/themes/pterodactyl/css/terminal.css +++ /dev/null @@ -1,104 +0,0 @@ -/*Design for Terminal*/ -@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro'); - -#terminal-body { - background: rgb(26, 26, 26); - margin: 0; - width: 100%; - height: 100%; - overflow: hidden; -} - -#terminal { - font-family: 'Source Code Pro', monospace; - color: rgb(223, 223, 223); - background: rgb(26, 26, 26); - font-size: 12px; - line-height: 14px; - padding: 10px 10px 0; - box-sizing: border-box; - height: 500px; - max-height: 500px; - overflow-y: auto; - overflow-x: hidden; - border-radius: 5px 5px 0 0; -} - -#terminal > .cmd { - padding: 1px 0; - word-wrap: break-word; -} - -#terminal_input { - width: 100%; - background: rgb(26, 26, 26); - border-radius: 0 0 5px 5px; - padding: 0 0 0 10px !important; -} - -.terminal_input--input, .terminal_input--prompt { - font-family: 'Source Code Pro', monospace; - margin-bottom: 0; - border: 0 !important; - background: transparent !important; - color: rgb(223, 223, 223); - font-size: 12px; - padding: 1px 0 4px !important; -} -.terminal_input--input { - margin-left: 6px; - line-height: 1; - outline: none !important; -} - -.terminal-notify { - position: absolute; - right: 30px; - bottom: 30px; - padding: 3.5px 7px; - border-radius: 3px; - background: #fff; - color: #000; - opacity: .5; - font-size: 16px; - cursor: pointer; - z-index: 10; -} - -.terminal-notify:hover { - opacity: .9; -} - -.ansi-black-fg { color: rgb(0, 0, 0); } -.ansi-red-fg { color: rgb(166, 0, 44); } -.ansi-green-fg { color: rgb(55, 106, 27); } -.ansi-yellow-fg { color: rgb(241, 133, 24); } -.ansi-blue-fg { color: rgb(17, 56, 163); } -.ansi-magenta-fg { color: rgb(67, 0, 117); } -.ansi-cyan-fg { color: rgb(18, 95, 105); } -.ansi-white-fg { color: rgb(255, 255, 255); } -.ansi-bright-black-fg { color: rgb(51, 51, 51); } -.ansi-bright-red-fg { color: rgb(223, 45, 39); } -.ansi-bright-green-fg { color: rgb(105, 175, 45); } -.ansi-bright-yellow-fg { color: rgb(254, 232, 57); } -.ansi-bright-blue-fg { color: rgb(68, 145, 240); } -.ansi-bright-magenta-fg { color: rgb(151, 50, 174); } -.ansi-bright-cyan-fg{ color: rgb(37, 173, 98); } -.ansi-bright-white-fg { color: rgb(208, 208, 208); } - -.ansi-black-bg { background: rgb(0, 0, 0); } -.ansi-red-bg { background: rgb(166, 0, 44); } -.ansi-green-bg { background: rgb(55, 106, 27); } -.ansi-yellow-bg { background: rgb(241, 133, 24); } -.ansi-blue-bg { background: rgb(17, 56, 163); } -.ansi-magenta-bg { background: rgb(67, 0, 117); } -.ansi-cyan-bg { background: rgb(18, 95, 105); } -.ansi-white-bg { background: rgb(255, 255, 255); } -.ansi-bright-black-bg { background: rgb(51, 51, 51); } -.ansi-bright-red-bg { background: rgb(223, 45, 39); } -.ansi-bright-green-bg { background: rgb(105, 175, 45); } -.ansi-bright-yellow-bg { background: rgb(254, 232, 57); } -.ansi-bright-blue-bg { background: rgb(68, 145, 240); } -.ansi-bright-magenta-bg { background: rgb(151, 50, 174); } -.ansi-bright-cyan-bg { background: rgb(37, 173, 98); } -.ansi-bright-white-bg { background: rgb(208, 208, 208); } diff --git a/public/themes/pterodactyl/js/admin/functions.js b/public/themes/pterodactyl/js/admin/functions.js deleted file mode 100644 index 26115cd46..000000000 --- a/public/themes/pterodactyl/js/admin/functions.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -$.urlParam=function(name){var results=new RegExp("[\\?&]"+name+"=([^&#]*)").exec(decodeURIComponent(window.location.href));if(results==null){return null}else{return results[1]||0}};function getPageName(url){var index=url.lastIndexOf("/")+1;var filenameWithExtension=url.substr(index);var filename=filenameWithExtension.split(".")[0];return filename} -// Remeber Active Tab and Navigate to it on Reload -for(var queryParameters={},queryString=location.search.substring(1),re=/([^&=]+)=([^&]*)/g,m;m=re.exec(queryString);)queryParameters[decodeURIComponent(m[1])]=decodeURIComponent(m[2]);$("a[data-toggle='tab']").click(function(){queryParameters.tab=$(this).attr("href").substring(1),window.history.pushState(null,null,location.pathname+"?"+$.param(queryParameters))}); -if($.urlParam('tab') != null){$('.nav.nav-tabs a[href="#' + $.urlParam('tab') + '"]').tab('show');} diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js deleted file mode 100644 index 97f05487b..000000000 --- a/public/themes/pterodactyl/js/admin/new-server.js +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -$(document).ready(function() { - $('#pNestId').select2({ - placeholder: 'Select a Nest', - }).change(); - $('#pEggId').select2({ - placeholder: 'Select a Nest Egg', - }); - $('#pPackId').select2({ - placeholder: 'Select a Service Pack', - }); - $('#pNodeId').select2({ - placeholder: 'Select a Node', - }).change(); - $('#pAllocation').select2({ - placeholder: 'Select a Default Allocation', - }); - $('#pAllocationAdditional').select2({ - placeholder: 'Select Additional Allocations', - }); - - $('#pUserId').select2({ - ajax: { - url: Router.route('admin.users.json'), - dataType: 'json', - delay: 250, - data: function (params) { - return { - q: params.term, // search term - page: params.page, - }; - }, - processResults: function (data, params) { - return { results: data }; - }, - cache: true, - }, - escapeMarkup: function (markup) { return markup; }, - minimumInputLength: 2, - templateResult: function (data) { - if (data.loading) return data.text; - - return '
\ - User Image \ - \ - ' + data.name_first + ' ' + data.name_last +' \ - \ - ' + data.email + ' - ' + data.username + ' \ -
'; - }, - templateSelection: function (data) { - return '
\ - \ - User Image \ - \ - \ - ' + data.name_first + ' ' + data.name_last + ' (' + data.email + ') \ - \ -
'; - } - }); -}); - -var lastActiveBox = null; -$(document).on('click', function (event) { - if (lastActiveBox !== null) { - lastActiveBox.removeClass('box-primary'); - } - - lastActiveBox = $(event.target).closest('.box'); - lastActiveBox.addClass('box-primary'); -}); - -$('#pNodeId').on('change', function () { - currentNode = $(this).val(); - $.each(Pterodactyl.nodeData, function (i, v) { - if (v.id == currentNode) { - $('#pAllocation').html('').select2({ - data: v.allocations, - placeholder: 'Select a Default Allocation', - }); - $('#pAllocationAdditional').html('').select2({ - data: v.allocations, - placeholder: 'Select Additional Allocations', - }) - } - }); -}); - -$('#pNestId').on('change', function (event) { - $('#pEggId').html('').select2({ - data: $.map(_.get(Pterodactyl.nests, $(this).val() + '.eggs', []), function (item) { - return { - id: item.id, - text: item.name, - }; - }), - }).change(); -}); - -$('#pEggId').on('change', function (event) { - var parentChain = _.get(Pterodactyl.nests, $('#pNestId').val(), null); - var objectChain = _.get(parentChain, 'eggs.' + $(this).val(), null); - - $('#pDefaultContainer').val(_.get(objectChain, 'docker_image', 'not defined!')); - - if (!_.get(objectChain, 'startup', false)) { - $('#pStartup').val(_.get(parentChain, 'startup', 'ERROR: Startup Not Defined!')); - } else { - $('#pStartup').val(_.get(objectChain, 'startup')); - } - - $('#pPackId').html('').select2({ - data: [{ id: 0, text: 'No Service Pack' }].concat( - $.map(_.get(objectChain, 'packs', []), function (item, i) { - return { - id: item.id, - text: item.name + ' (' + item.version + ')', - }; - }) - ), - }); - - $('#appendVariablesTo').html(''); - $.each(_.get(objectChain, 'variables', []), function (i, item) { - var isRequired = (item.required === 1) ? 'Required ' : ''; - var dataAppend = ' \ -
\ - \ - \ -

' + item.description + '
\ - Access in Startup: {{' + item.env_variable + '}}
\ - Validation Rules: ' + item.rules + '

\ -
\ - '; - $('#appendVariablesTo').append(dataAppend); - }); -}); diff --git a/public/themes/pterodactyl/js/admin/node/view-servers.js b/public/themes/pterodactyl/js/admin/node/view-servers.js deleted file mode 100644 index 96950cd19..000000000 --- a/public/themes/pterodactyl/js/admin/node/view-servers.js +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -(function initSocket() { - if (typeof $.notifyDefaults !== 'function') { - console.error('Notify does not appear to be loaded.'); - return; - } - - if (typeof io !== 'function') { - console.error('Socket.io is reqired to use this panel.'); - return; - } - - $.notifyDefaults({ - placement: { - from: 'bottom', - align: 'right' - }, - newest_on_top: true, - delay: 2000, - animate: { - enter: 'animated zoomInDown', - exit: 'animated zoomOutDown' - } - }); - - var notifySocketError = false; - // Main Socket Object - window.Socket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/v1/stats/', { - 'query': 'token=' + Pterodactyl.node.daemonSecret, - }); - - // Socket Failed to Connect - Socket.io.on('connect_error', function (err) { - if(typeof notifySocketError !== 'object') { - notifySocketError = $.notify({ - message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.

' + err, - }, { - type: 'danger', - delay: 0 - }); - } - }); - - // Connected to Socket Successfully - Socket.on('connect', function () { - if (notifySocketError !== false) { - notifySocketError.close(); - notifySocketError = false; - } - }); - - Socket.on('error', function (err) { - console.error('There was an error while attemping to connect to the websocket: ' + err + '\n\nPlease try loading this page again.'); - }); - - Socket.on('live-stats', function (data) { - $.each(data.servers, function (uuid, info) { - var element = $('tr[data-server="' + uuid + '"]'); - switch (info.status) { - case 0: - element.find('[data-action="status"]').html('Offline'); - break; - case 1: - element.find('[data-action="status"]').html('Online'); - break; - case 2: - element.find('[data-action="status"]').html('Starting'); - break; - case 3: - element.find('[data-action="status"]').html('Stopping'); - break; - case 20: - element.find('[data-action="status"]').html('Installing'); - break; - case 30: - element.find('[data-action="status"]').html('Suspended'); - break; - } - if (info.status !== 0) { - var cpuMax = element.find('[data-action="cpu"]').data('cpumax'); - var currentCpu = info.proc.cpu.total; - if (cpuMax !== 0) { - currentCpu = parseFloat(((info.proc.cpu.total / cpuMax) * 100).toFixed(2).toString()); - } - element.find('[data-action="memory"]').html(parseInt(info.proc.memory.total / (1024 * 1024))); - element.find('[data-action="disk"]').html(parseInt(info.proc.disk.used)); - element.find('[data-action="cpu"]').html(currentCpu); - } else { - element.find('[data-action="memory"]').html('--'); - element.find('[data-action="disk"]').html('--'); - element.find('[data-action="cpu"]').html('--'); - } - }); - }); -})(); \ No newline at end of file diff --git a/public/themes/pterodactyl/js/admin/statistics.js b/public/themes/pterodactyl/js/admin/statistics.js deleted file mode 100644 index 7433f3221..000000000 --- a/public/themes/pterodactyl/js/admin/statistics.js +++ /dev/null @@ -1,118 +0,0 @@ -var freeDisk = Pterodactyl.totalNodeDisk - Pterodactyl.totalServerDisk; -let diskChart = new Chart($('#disk_chart'), { - type: 'pie', - data: { - labels: ['Free Disk', 'Used Disk'], - datasets: [ - { - label: 'Disk (in MB)', - backgroundColor: ['#51B060', '#ff0000'], - data: [freeDisk, Pterodactyl.totalServerDisk] - } - ] - } -}); - -var freeRam = Pterodactyl.totalNodeRam - Pterodactyl.totalServerRam; -let ramChart = new Chart($('#ram_chart'), { - type: 'pie', - data: { - labels: ['Free RAM', 'Used RAM'], - datasets: [ - { - label: 'Memory (in MB)', - backgroundColor: ['#51B060', '#ff0000'], - data: [freeRam, Pterodactyl.totalServerRam] - } - ] - } -}); - -var activeServers = Pterodactyl.servers.length - Pterodactyl.suspendedServers; -let serversChart = new Chart($('#servers_chart'), { - type: 'pie', - data: { - labels: ['Active', 'Suspended'], - datasets: [ - { - label: 'Servers', - backgroundColor: ['#51B060', '#E08E0B'], - data: [activeServers, Pterodactyl.suspendedServers] - } - ] - } -}); - -let statusChart = new Chart($('#status_chart'), { - type: 'pie', - data: { - labels: ['Online', 'Offline', 'Installing', 'Error'], - datasets: [ - { - label: '', - backgroundColor: ['#51B060', '#b7b7b7', '#E08E0B', '#ff0000'], - data: [0,0,0,0] - } - ] - } -}); - -var servers = Pterodactyl.servers; -var nodes = Pterodactyl.nodes; - -for (let i = 0; i < servers.length; i++) { - setTimeout(getStatus, 200 * i, servers[i]); -} - -function getStatus(server) { - var uuid = server.uuid; - var node = getNodeByID(server.node_id); - - $.ajax({ - type: 'GET', - url: node.scheme + '://' + node.fqdn + ':'+node.daemonListen+'/v1/server', - timeout: 5000, - headers: { - 'X-Access-Server': uuid, - 'X-Access-Token': Pterodactyl.tokens[node.id], - } - }).done(function (data) { - - if (typeof data.status === 'undefined') { - // Error - statusChart.data.datasets[0].data[3]++; - return; - } - - switch (data.status) { - case 0: - case 3: - case 30: - // Offline - statusChart.data.datasets[0].data[1]++; - break; - case 1: - case 2: - // Online - statusChart.data.datasets[0].data[0]++; - break; - case 20: - // Installing - statusChart.data.datasets[0].data[2]++; - break; - } - statusChart.update(); - }).fail(function (jqXHR) { - // Error - statusChart.data.datasets[0].data[3]++; - statusChart.update(); - }); -} - -function getNodeByID(id) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].id === id) { - return nodes[i]; - } - } -} \ No newline at end of file diff --git a/public/themes/pterodactyl/js/frontend/2fa-modal.js b/public/themes/pterodactyl/js/frontend/2fa-modal.js deleted file mode 100644 index 8de4ee539..000000000 --- a/public/themes/pterodactyl/js/frontend/2fa-modal.js +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. - -var TwoFactorModal = (function () { - - function bindListeners() { - $(document).ready(function () { - $('#close_reload').click(function () { - location.reload(); - }); - $('#do_2fa').submit(function (event) { - event.preventDefault(); - - $.ajax({ - type: 'PUT', - url: Router.route('account.security.totp'), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - } - }).done(function (data) { - var image = new Image(); - image.src = data.qrImage; - $(image).on('load', function () { - $('#hide_img_load').slideUp(function () { - $('#qr_image_insert').attr('src', image.src).slideDown(); - }); - }); - $('#open2fa').modal('show'); - }).fail(function (jqXHR) { - alert('An error occurred while attempting to load the 2FA setup modal. Please try again.'); - console.error(jqXHR); - }); - - }); - $('#2fa_token_verify').submit(function (event) { - event.preventDefault(); - $('#submit_action').html(' Submit').addClass('disabled'); - - $.ajax({ - type: 'POST', - url: Router.route('account.security.totp'), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - }, - data: { - token: $('#2fa_token').val() - } - }).done(function (data) { - $('#notice_box_2fa').hide(); - if (data === 'true') { - $('#notice_box_2fa').html('
2-Factor Authentication has been enabled on your account. Press \'Close\' below to reload the page.
').slideDown(); - } else { - $('#notice_box_2fa').html('
The token provided was invalid.
').slideDown(); - } - }).fail(function (jqXHR) { - $('#notice_box_2fa').html('
There was an error while attempting to enable 2-Factor Authentication on this account.
').slideDown(); - console.error(jqXHR); - }).always(function () { - $('#submit_action').html('Submit').removeClass('disabled'); - }); - }); - }); - } - - return { - init: function () { - bindListeners(); - } - } -})(); - -TwoFactorModal.init(); diff --git a/public/themes/pterodactyl/js/frontend/console.js b/public/themes/pterodactyl/js/frontend/console.js deleted file mode 100644 index 19a11e20c..000000000 --- a/public/themes/pterodactyl/js/frontend/console.js +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -var CONSOLE_PUSH_COUNT = Pterodactyl.config.console_count || 50; -var CONSOLE_PUSH_FREQ = Pterodactyl.config.console_freq || 200; -var CONSOLE_OUTPUT_LIMIT = Pterodactyl.config.console_limit || 2000; - -var KEYCODE_UP_ARROW = 38; -var KEYCODE_DOWN_ARROW = 40; - -var AnsiUp = new AnsiUp; -AnsiUp.use_classes = true; - -var $terminal = $('#terminal'); -var $terminalInput = $('.terminal_input--input'); -var $scrollNotify = $('#terminalNotify'); - -$(document).ready(function () { - var storage = window.localStorage; - var activeHx = []; - var currentHxIndex = 0; - var currentKeyCount = 0; - - var storedConsoleHistory = storage.getItem('console_hx_' + Pterodactyl.server.uuid); - try { - activeHx = JSON.parse(storedConsoleHistory) || []; - currentKeyCount = activeHx.length - 1; - } catch (ex) { - // - } - - $terminalInput.focus(); - $('.terminal_input--prompt, #terminal_input, #terminalNotify').on('click', function () { - $terminalInput.focus(); - }); - - $terminalInput.on('keyup', function (e) { - if (e.which === KEYCODE_DOWN_ARROW || e.which === KEYCODE_UP_ARROW) { - var value = consoleHistory(e.which); - - if (value !== false) { - $terminalInput.val(value); - } - } - - if (e.which === 27) { - $(this).val(''); - } - - if (e.which === 13) { - saveToHistory($(this).val()); - Socket.emit((ConsoleServerStatus !== 0) ? 'send command' : 'set status', $(this).val()); - - $(this).val(''); - } - }); - - function consoleHistory(key) { - // Get previous - if (key === KEYCODE_UP_ARROW) { - // currentHxIndex++; - var index = activeHx.length - (currentHxIndex + 1); - - if (typeof activeHx[index - 1] === 'undefined') { - return activeHx[index]; - } - - currentHxIndex++; - return activeHx[index]; - } - - // Get more recent - if (key === KEYCODE_DOWN_ARROW) { - var index = activeHx.length - currentHxIndex; - - if (typeof activeHx[index + 1] === 'undefined') { - return activeHx[index]; - } - - currentHxIndex--; - return activeHx[index]; - } - } - - function saveToHistory(command) { - if (command.length === 0) { - return; - } - - if (activeHx.length >= 50) { - activeHx.pop(); - } - - currentHxIndex = 0; - currentKeyCount++; - activeHx[currentKeyCount] = command; - - storage.setItem('console_hx_' + Pterodactyl.server.uuid, JSON.stringify(activeHx)); - } -}); - -$terminal.on('scroll', function () { - if (isTerminalScrolledDown()) { - $scrollNotify.addClass('hidden'); - } -}); - -function isTerminalScrolledDown() { - return $terminal.scrollTop() + $terminal.innerHeight() + 50 > $terminal[0].scrollHeight; -} - -window.scrollToBottom = function () { - $terminal.scrollTop($terminal[0].scrollHeight); -}; - -function pushToTerminal(string) { - $terminal.append('
' + AnsiUp.ansi_to_html(string + '\u001b[0m') + '
'); -} - -(function initConsole() { - window.TerminalQueue = []; - window.ConsoleServerStatus = 0; - window.ConsoleElements = 0; - - $scrollNotify.on('click', function () { - window.scrollToBottom(); - $scrollNotify.addClass('hidden'); - }); -})(); - -(function pushOutputQueue() { - if (TerminalQueue.length > CONSOLE_PUSH_COUNT) { - // console throttled warning show - } - - if (TerminalQueue.length > 0) { - var scrolledDown = isTerminalScrolledDown(); - - for (var i = 0; i < CONSOLE_PUSH_COUNT && TerminalQueue.length > 0; i++) { - pushToTerminal(TerminalQueue[0]); - - window.ConsoleElements++; - TerminalQueue.shift(); - } - - if (scrolledDown) { - window.scrollToBottom(); - } else if ($scrollNotify.hasClass('hidden')) { - $scrollNotify.removeClass('hidden'); - } - - var removeElements = window.ConsoleElements - CONSOLE_OUTPUT_LIMIT; - if (removeElements > 0) { - $('#terminal').find('.cmd').slice(0, removeElements).remove(); - window.ConsoleElements = window.ConsoleElements - removeElements; - } - } - - window.setTimeout(pushOutputQueue, CONSOLE_PUSH_FREQ); -})(); - -(function setupSocketListeners() { - // Update Listings on Initial Status - Socket.on('initial status', function (data) { - ConsoleServerStatus = data.status; - updateServerPowerControls(data.status); - - if (data.status === 1 || data.status === 2) { - Socket.emit('send server log'); - } - }); - - // Update Listings on Status - Socket.on('status', function (data) { - ConsoleServerStatus = data.status; - updateServerPowerControls(data.status); - }); - - // Skips the queue so we don't wait - // 10 minutes to load the log... - Socket.on('server log', function (data) { - $('#terminal').html(''); - data.split(/\n/g).forEach(function (item) { - pushToTerminal(item); - }); - window.scrollToBottom(); - }); - - Socket.on('console', function (data) { - if(data.line) { - data.line.split(/\n/g).forEach(function (item) { - TerminalQueue.push(item); - }); - } - }); -})(); - -function updateServerPowerControls (data) { - // Server is On or Starting - if(data == 1 || data == 2) { - $('[data-attr="power"][data-action="start"]').addClass('disabled'); - $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').removeClass('disabled'); - } else { - if (data == 0) { - $('[data-attr="power"][data-action="start"]').removeClass('disabled'); - } - $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').addClass('disabled'); - } - - if(data !== 0) { - $('[data-attr="power"][data-action="kill"]').removeClass('disabled'); - } else { - $('[data-attr="power"][data-action="kill"]').addClass('disabled'); - } -} - -$(document).ready(function () { - $('[data-attr="power"]').click(function (event) { - if (! $(this).hasClass('disabled')) { - Socket.emit('set status', $(this).data('action')); - } - }); - - (function setupChartElements() { - if (typeof SkipConsoleCharts !== 'undefined') { - return; - } - - Socket.on('proc', function (proc) { - if (CPUData.length > 10) { - CPUData.shift(); - MemoryData.shift(); - TimeLabels.shift(); - } - - var cpuUse = (Pterodactyl.server.cpu > 0) ? parseFloat(((proc.data.cpu.total / Pterodactyl.server.cpu) * 100).toFixed(3).toString()) : proc.data.cpu.total; - CPUData.push(cpuUse); - MemoryData.push(parseInt(proc.data.memory.total / (1024 * 1024))); - - TimeLabels.push($.format.date(new Date(), 'HH:mm:ss')); - - - // memory.cmax is the maximum given by the container - // memory.amax is given by the json config - // use the maximum of both - // with no limit memory.cmax will always be higher - // but with limit memory.amax is sometimes still smaller than memory.total - MemoryChart.config.options.scales.yAxes[0].ticks.max = Math.max(proc.data.memory.cmax, proc.data.memory.amax) / (1000 * 1000); - - if (Pterodactyl.server.cpu > 0) { - // if there is a cpu limit defined use 100% as maximum - CPUChart.config.options.scales.yAxes[0].ticks.max = 100; - } else { - // if there is no cpu limit defined use linux percentage - // and find maximum in all values - var maxCpu = 1; - for(var i = 0; i < CPUData.length; i++) { - maxCpu = Math.max(maxCpu, parseFloat(CPUData[i])) - } - - maxCpu = Math.ceil(maxCpu / 100) * 100; - CPUChart.config.options.scales.yAxes[0].ticks.max = maxCpu; - } - - - - CPUChart.update(); - MemoryChart.update(); - }); - - var ctc = $('#chart_cpu'); - var TimeLabels = []; - var CPUData = []; - var CPUChart = new Chart(ctc, { - type: 'line', - data: { - labels: TimeLabels, - datasets: [ - { - label: "Percent Use", - fill: false, - lineTension: 0.03, - backgroundColor: "#3c8dbc", - borderColor: "#3c8dbc", - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - pointBorderColor: "#3c8dbc", - pointBackgroundColor: "#fff", - pointBorderWidth: 1, - pointHoverRadius: 5, - pointHoverBackgroundColor: "#3c8dbc", - pointHoverBorderColor: "rgba(220,220,220,1)", - pointHoverBorderWidth: 2, - pointRadius: 1, - pointHitRadius: 10, - data: CPUData, - spanGaps: false, - } - ] - }, - options: { - title: { - display: true, - text: 'CPU Usage (as Percent Total)' - }, - legend: { - display: false, - }, - animation: { - duration: 1, - }, - scales: { - yAxes: [{ - ticks: { - beginAtZero: true - } - }] - } - } - }); - - var ctm = $('#chart_memory'); - MemoryData = []; - MemoryChart = new Chart(ctm, { - type: 'line', - data: { - labels: TimeLabels, - datasets: [ - { - label: "Memory Use", - fill: false, - lineTension: 0.03, - backgroundColor: "#3c8dbc", - borderColor: "#3c8dbc", - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - pointBorderColor: "#3c8dbc", - pointBackgroundColor: "#fff", - pointBorderWidth: 1, - pointHoverRadius: 5, - pointHoverBackgroundColor: "#3c8dbc", - pointHoverBorderColor: "rgba(220,220,220,1)", - pointHoverBorderWidth: 2, - pointRadius: 1, - pointHitRadius: 10, - data: MemoryData, - spanGaps: false, - } - ] - }, - options: { - title: { - display: true, - text: 'Memory Usage (in Megabytes)' - }, - legend: { - display: false, - }, - animation: { - duration: 1, - }, - scales: { - yAxes: [{ - ticks: { - beginAtZero: true - } - }] - } - } - }); - })(); -}); diff --git a/public/themes/pterodactyl/js/frontend/files/editor.js b/public/themes/pterodactyl/js/frontend/files/editor.js deleted file mode 100644 index 0ab7f9f7a..000000000 --- a/public/themes/pterodactyl/js/frontend/files/editor.js +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -(function () { - window.Editor = ace.edit('editor'); - var Whitespace = ace.require('ace/ext/whitespace'); - var Modelist = ace.require('ace/ext/modelist'); - - Editor.setTheme('ace/theme/chrome'); - Editor.getSession().setUseWrapMode(true); - Editor.setShowPrintMargin(false); - - if (typeof Pterodactyl !== 'undefined') { - if(typeof Pterodactyl.stat !== 'undefined') { - Editor.getSession().setMode(Modelist.getModeForPath(Pterodactyl.stat.name).mode); - } - } - - Editor.commands.addCommand({ - name: 'save', - bindKey: {win: 'Ctrl-S', mac: 'Command-S'}, - exec: function(editor) { - if ($('#save_file').length) { - save(); - } else if ($('#create_file').length) { - create(); - } - }, - readOnly: false - }); - - Editor.commands.addCommands(Whitespace.commands); - - Whitespace.detectIndentation(Editor.session); - - $('#save_file').on('click', function (e) { - e.preventDefault(); - save(); - }); - - $('#create_file').on('click', function (e) { - e.preventDefault(); - create(); - }); - - $('#aceMode').on('change', event => { - Editor.getSession().setMode('ace/mode/' + $('#aceMode').val()); - }); - - function create() { - if (_.isEmpty($('#file_name').val())) { - $.notify({ - message: 'No filename was passed.' - }, { - type: 'danger' - }); - return; - } - $('#create_file').html(' Creating File').addClass('disabled'); - $.ajax({ - type: 'POST', - url: Router.route('server.files.save', { server: Pterodactyl.server.uuidShort }), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - }, - data: { - file: $('#file_name').val(), - contents: Editor.getValue() - } - }).done(function (data) { - window.location.replace(Router.route('server.files.edit', { - server: Pterodactyl.server.uuidShort, - file: $('#file_name').val(), - })); - }).fail(function (jqXHR) { - $.notify({ - message: jqXHR.responseText - }, { - type: 'danger' - }); - }).always(function () { - $('#create_file').html('Create File').removeClass('disabled'); - }); - } - - function save() { - var fileName = $('input[name="file"]').val(); - $('#save_file').html(' Saving File').addClass('disabled'); - $.ajax({ - type: 'POST', - url: Router.route('server.files.save', { server: Pterodactyl.server.uuidShort }), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - }, - data: { - file: fileName, - contents: Editor.getValue() - } - }).done(function (data) { - $.notify({ - message: 'File was successfully saved.' - }, { - type: 'success' - }); - }).fail(function (jqXHR) { - $.notify({ - message: jqXHR.responseText - }, { - type: 'danger' - }); - }).always(function () { - $('#save_file').html('  Save File').removeClass('disabled'); - }); - } -})(); diff --git a/public/themes/pterodactyl/js/frontend/files/filemanager.min.js b/public/themes/pterodactyl/js/frontend/files/filemanager.min.js deleted file mode 100644 index 5fe4e9f35..000000000 --- a/public/themes/pterodactyl/js/frontend/files/filemanager.min.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i').text(value).html()}},{key:'folder',value:function folder(path){var inputValue=void 0;if(path){inputValue=path}else{var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.data('name'));var currentPath=decodeURIComponent(nameBlock.data('path'));if($(this.element).data('type')==='file'){inputValue=currentPath}else{inputValue=''+currentPath+currentName+'/'}}swal({type:'input',title:'Create Folder',text:'Please enter the path and folder name below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:inputValue},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/folder',timeout:10000,data:JSON.stringify({path:val})}).done(function(data){swal.close();Files.list()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'move',value:function move(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var currentPath=decodeURIComponent(nameBlock.data('path'));swal({type:'input',title:'Move File',text:'Please enter the new path for the file below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:''+currentPath+currentName},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/move',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+val})}).done(function(data){nameBlock.parent().addClass('warning').delay(200).fadeOut();swal.close()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'rename',value:function rename(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentLink=nameBlock.find('a');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var attachEditor='\n \n \n ';nameBlock.html(attachEditor);var inputField=nameBlock.find('input');var inputLoader=nameBlock.find('.input-loader');inputField.focus();inputField.on('blur keydown',function(e){if(e.type==='keydown'&&e.which===27||e.type==='blur'||e.type==='keydown'&&e.which===13&¤tName===inputField.val()){if(!_.isEmpty(currentLink)){nameBlock.html(currentLink)}else{nameBlock.html(currentName)}inputField.remove();ContextMenu.unbind().run();return}if(e.type==='keydown'&&e.which!==13)return;inputLoader.show();var currentPath=decodeURIComponent(nameBlock.data('path'));$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/rename',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+currentPath+inputField.val()})}).done(function(data){nameBlock.attr('data-name',inputField.val());if(!_.isEmpty(currentLink)){var newLink=currentLink.attr('href');if(nameBlock.parent().data('type')!=='folder'){newLink=newLink.substr(0,newLink.lastIndexOf('/'))+'/'+inputField.val()}currentLink.attr('href',newLink);nameBlock.html(currentLink.html(inputField.val()))}else{nameBlock.html(inputField.val())}inputField.remove()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}nameBlock.addClass('has-error').delay(2000).queue(function(){nameBlock.removeClass('has-error').dequeue()});inputField.popover({animation:true,placement:'top',content:error,title:'Save Error'}).popover('show')}).always(function(){inputLoader.remove();ContextMenu.unbind().run()})})}},{key:'copy',value:function copy(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var currentPath=decodeURIComponent(nameBlock.data('path'));swal({type:'input',title:'Copy File',text:'Please enter the new path for the copied file below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:''+currentPath+currentName},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/copy',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+val})}).done(function(data){swal({type:'success',title:'',text:'File successfully copied.'});Files.list()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'download',value:function download(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var fileName=decodeURIComponent(nameBlock.attr('data-name'));var filePath=decodeURIComponent(nameBlock.data('path'));window.location='/server/'+Pterodactyl.server.uuidShort+'/files/download/'+filePath+fileName}},{key:'delete',value:function _delete(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var delPath=decodeURIComponent(nameBlock.data('path'));var delName=decodeURIComponent(nameBlock.data('name'));swal({type:'warning',title:'',text:'Are you sure you want to delete '+this.sanitizedString(delName)+'?',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/delete',timeout:10000,data:JSON.stringify({items:[''+delPath+delName]})}).done(function(data){nameBlock.parent().addClass('warning').delay(200).fadeOut();swal({type:'success',title:'File Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occurred while attempting to delete this file. Please try again.'})})})}},{key:'toggleMassActions',value:function toggleMassActions(){if($('#file_listing input[type="checkbox"]:checked').length){$('#mass_actions').removeClass('disabled')}else{$('#mass_actions').addClass('disabled')}}},{key:'toggleHighlight',value:function toggleHighlight(event){var parent=$(event.currentTarget);var item=$(event.currentTarget).find('input');if($(item).is(':checked')){$(item).prop('checked',false);parent.removeClass('warning').delay(200)}else{$(item).prop('checked',true);parent.addClass('warning').delay(200)}}},{key:'highlightAll',value:function highlightAll(event){var parent=void 0;var item=$(event.currentTarget).find('input');if($(item).is(':checked')){$('#file_listing input[type=checkbox]').prop('checked',false);$('#file_listing input[data-action="addSelection"]').each(function(){parent=$(this).closest('tr');parent.removeClass('warning').delay(200)})}else{$('#file_listing input[type=checkbox]').prop('checked',true);$('#file_listing input[data-action="addSelection"]').each(function(){parent=$(this).closest('tr');parent.addClass('warning').delay(200)})}}},{key:'deleteSelected',value:function deleteSelected(){var selectedItems=[];var selectedItemsElements=[];var parent=void 0;var nameBlock=void 0;var delLocation=void 0;$('#file_listing input[data-action="addSelection"]:checked').each(function(){parent=$(this).closest('tr');nameBlock=$(parent).find('td[data-identifier="name"]');delLocation=decodeURIComponent(nameBlock.data('path'))+decodeURIComponent(nameBlock.data('name'));selectedItems.push(delLocation);selectedItemsElements.push(parent)});if(selectedItems.length!=0){var formattedItems='';var i=0;var self=this;$.each(selectedItems,function(key,value){formattedItems+=''+self.sanitizedString(value)+', ';i++;return i<5});formattedItems=formattedItems.slice(0,-2);if(selectedItems.length>5){formattedItems+=', and '+(selectedItems.length-5)+' other(s)'}swal({type:'warning',title:'',text:'Are you sure you want to delete the following files: '+formattedItems+'?',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/delete',timeout:10000,data:JSON.stringify({items:selectedItems})}).done(function(data){$('#file_listing input:checked').each(function(){$(this).prop('checked',false)});$.each(selectedItemsElements,function(){$(this).addClass('warning').delay(200).fadeOut()});swal({type:'success',title:'Files Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occurred while attempting to delete these files. Please try again.'})})})}else{swal({type:'warning',title:'',text:'Please select files/folders to delete.'})}}},{key:'decompress',value:function decompress(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));swal({title:' Decompressing...',text:'This might take a few seconds to complete.',html:true,allowOutsideClick:false,allowEscapeKey:false,showConfirmButton:false});$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/decompress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName})}).done(function(data){swal.close();Files.list(compPath)}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:error})})}},{key:'compress',value:function compress(){var _this=this;var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/compress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName,to:compPath.toString()})}).done(function(data){Files.list(compPath,function(err){if(err)return;var fileListing=$('#file_listing').find('[data-name="'+data.saved_as+'"]').parent();fileListing.addClass('success pulsate').delay(3000).queue(function(){fileListing.removeClass('success pulsate').dequeue()})})}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:_this.sanitizedString(error)})})}}]);return ActionsClass}(); -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i').text(newFilePath).html()+'" class="text-muted"> New File
  • New Folder
  • '}if(Pterodactyl.permissions.downloadFiles||Pterodactyl.permissions.deleteFiles){buildMenu+='
  • '}if(Pterodactyl.permissions.downloadFiles){buildMenu+=''}if(Pterodactyl.permissions.deleteFiles){buildMenu+='
  • Delete
  • '}buildMenu+='';return buildMenu}},{key:'rightClick',value:function rightClick(){var _this=this;$('[data-action="toggleMenu"]').on('mousedown',function(event){event.preventDefault();if($(document).find('#fileOptionMenu').is(':visible')){$('body').trigger('click');return}_this.showMenu(event)});$('#file_listing > tbody td').on('contextmenu',function(event){_this.showMenu(event)})}},{key:'showMenu',value:function showMenu(event){var _this2=this;var parent=$(event.target).closest('tr');var menu=$(this.makeMenu(parent));if(parent.data('type')==='disabled')return;event.preventDefault();$(menu).appendTo('body');$(menu).data('invokedOn',$(event.target)).show().css({position:'absolute',left:event.pageX-150,top:event.pageY});this.activeLine=parent;this.activeLine.addClass('active');var Actions=new ActionsClass(parent,menu);if(Pterodactyl.permissions.moveFiles){$(menu).find('li[data-action="move"]').unbind().on('click',function(e){e.preventDefault();Actions.move()});$(menu).find('li[data-action="rename"]').unbind().on('click',function(e){e.preventDefault();Actions.rename()})}if(Pterodactyl.permissions.copyFiles){$(menu).find('li[data-action="copy"]').unbind().on('click',function(e){e.preventDefault();Actions.copy()})}if(Pterodactyl.permissions.compressFiles){if(parent.data('type')==='folder'){$(menu).find('li[data-action="compress"]').removeClass('hidden')}$(menu).find('li[data-action="compress"]').unbind().on('click',function(e){e.preventDefault();Actions.compress()})}if(Pterodactyl.permissions.decompressFiles){if(_.without(['application/zip','application/gzip','application/x-gzip'],parent.data('mime')).length<3){$(menu).find('li[data-action="decompress"]').removeClass('hidden')}$(menu).find('li[data-action="decompress"]').unbind().on('click',function(e){e.preventDefault();Actions.decompress()})}if(Pterodactyl.permissions.createFiles){$(menu).find('li[data-action="folder"]').unbind().on('click',function(e){e.preventDefault();Actions.folder()})}if(Pterodactyl.permissions.downloadFiles){if(parent.data('type')==='file'){$(menu).find('li[data-action="download"]').removeClass('hidden')}$(menu).find('li[data-action="download"]').unbind().on('click',function(e){e.preventDefault();Actions.download()})}if(Pterodactyl.permissions.deleteFiles){$(menu).find('li[data-action="delete"]').unbind().on('click',function(e){e.preventDefault();Actions.delete()})}$(window).unbind().on('click',function(event){if($(event.target).is('.disable-menu-hide')){event.preventDefault();return}$(menu).unbind().remove();if(!_.isNull(_this2.activeLine))_this2.activeLine.removeClass('active')})}},{key:'directoryClick',value:function directoryClick(){$('a[data-action="directory-view"]').on('click',function(event){event.preventDefault();var path=$(this).parent().data('path')||'';var name=$(this).parent().data('name')||'';window.location.hash=encodeURIComponent(path+name);Files.list()})}}]);return ContextMenuClass}();window.ContextMenu=new ContextMenuClass; -'use strict';var _typeof=typeof Symbol==='function'&&typeof Symbol.iterator==='symbol'?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==='function'&&obj.constructor===Symbol&&obj!==Symbol.prototype?'symbol':typeof obj};var _createClass=function(){function defineProperties(target,props){for(var i=0;i\r\n//\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy\r\n// of this software and associated documentation files (the \"Software\"), to deal\r\n// in the Software without restriction, including without limitation the rights\r\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n// copies of the Software, and to permit persons to whom the Software is\r\n// furnished to do so, subject to the following conditions:\r\n//\r\n// The above copyright notice and this permission notice shall be included in all\r\n// copies or substantial portions of the Software.\r\n//\r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n// SOFTWARE.\r\nclass ActionsClass {\r\n constructor(element, menu) {\r\n this.element = element;\r\n this.menu = menu;\r\n }\r\n\r\n destroy() {\r\n this.element = undefined;\r\n }\r\n\r\n sanitizedString(value) {\r\n return $('
    ').text(value).html();\r\n }\r\n\r\n folder(path) {\r\n let inputValue\r\n if (path) {\r\n inputValue = path\r\n } else {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const currentName = decodeURIComponent(nameBlock.data('name'));\r\n const currentPath = decodeURIComponent(nameBlock.data('path'));\r\n\r\n if ($(this.element).data('type') === 'file') {\r\n inputValue = currentPath;\r\n } else {\r\n inputValue = `${currentPath}${currentName}/`;\r\n }\r\n }\r\n\r\n swal({\r\n type: 'input',\r\n title: 'Create Folder',\r\n text: 'Please enter the path and folder name below.',\r\n showCancelButton: true,\r\n showConfirmButton: true,\r\n closeOnConfirm: false,\r\n showLoaderOnConfirm: true,\r\n inputValue: inputValue\r\n }, (val) => {\r\n if (val === false) {\r\n return false;\r\n }\r\n\r\n $.ajax({\r\n type: 'POST',\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/folder`,\r\n timeout: 10000,\r\n data: JSON.stringify({\r\n path: val,\r\n }),\r\n }).done(data => {\r\n swal.close();\r\n Files.list();\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n var error = 'An error occurred while trying to process this request.';\r\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\r\n error = jqXHR.responseJSON.error;\r\n }\r\n swal({\r\n type: 'error',\r\n title: '',\r\n text: error,\r\n });\r\n });\r\n });\r\n }\r\n\r\n move() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\r\n const currentPath = decodeURIComponent(nameBlock.data('path'));\r\n\r\n swal({\r\n type: 'input',\r\n title: 'Move File',\r\n text: 'Please enter the new path for the file below.',\r\n showCancelButton: true,\r\n showConfirmButton: true,\r\n closeOnConfirm: false,\r\n showLoaderOnConfirm: true,\r\n inputValue: `${currentPath}${currentName}`,\r\n }, (val) => {\r\n if (val === false) {\r\n return false;\r\n }\r\n\r\n $.ajax({\r\n type: 'POST',\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/move`,\r\n timeout: 10000,\r\n data: JSON.stringify({\r\n from: `${currentPath}${currentName}`,\r\n to: `${val}`,\r\n }),\r\n }).done(data => {\r\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\r\n swal.close();\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n var error = 'An error occurred while trying to process this request.';\r\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\r\n error = jqXHR.responseJSON.error;\r\n }\r\n swal({\r\n type: 'error',\r\n title: '',\r\n text: error,\r\n });\r\n });\r\n });\r\n\r\n }\r\n\r\n rename() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const currentLink = nameBlock.find('a');\r\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\r\n const attachEditor = `\r\n \r\n \r\n `;\r\n\r\n nameBlock.html(attachEditor);\r\n const inputField = nameBlock.find('input');\r\n const inputLoader = nameBlock.find('.input-loader');\r\n\r\n inputField.focus();\r\n inputField.on('blur keydown', e => {\r\n // Save Field\r\n if (\r\n (e.type === 'keydown' && e.which === 27)\r\n || e.type === 'blur'\r\n || (e.type === 'keydown' && e.which === 13 && currentName === inputField.val())\r\n ) {\r\n if (!_.isEmpty(currentLink)) {\r\n nameBlock.html(currentLink);\r\n } else {\r\n nameBlock.html(currentName);\r\n }\r\n inputField.remove();\r\n ContextMenu.unbind().run();\r\n return;\r\n }\r\n\r\n if (e.type === 'keydown' && e.which !== 13) return;\r\n\r\n inputLoader.show();\r\n const currentPath = decodeURIComponent(nameBlock.data('path'));\r\n\r\n $.ajax({\r\n type: 'POST',\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/rename`,\r\n timeout: 10000,\r\n data: JSON.stringify({\r\n from: `${currentPath}${currentName}`,\r\n to: `${currentPath}${inputField.val()}`,\r\n }),\r\n }).done(data => {\r\n nameBlock.attr('data-name', inputField.val());\r\n if (!_.isEmpty(currentLink)) {\r\n let newLink = currentLink.attr('href');\r\n if (nameBlock.parent().data('type') !== 'folder') {\r\n newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val();\r\n }\r\n currentLink.attr('href', newLink);\r\n nameBlock.html(\r\n currentLink.html(inputField.val())\r\n );\r\n } else {\r\n nameBlock.html(inputField.val());\r\n }\r\n inputField.remove();\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n var error = 'An error occurred while trying to process this request.';\r\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\r\n error = jqXHR.responseJSON.error;\r\n }\r\n nameBlock.addClass('has-error').delay(2000).queue(() => {\r\n nameBlock.removeClass('has-error').dequeue();\r\n });\r\n inputField.popover({\r\n animation: true,\r\n placement: 'top',\r\n content: error,\r\n title: 'Save Error'\r\n }).popover('show');\r\n }).always(() => {\r\n inputLoader.remove();\r\n ContextMenu.unbind().run();\r\n });\r\n });\r\n }\r\n\r\n copy() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\r\n const currentPath = decodeURIComponent(nameBlock.data('path'));\r\n\r\n swal({\r\n type: 'input',\r\n title: 'Copy File',\r\n text: 'Please enter the new path for the copied file below.',\r\n showCancelButton: true,\r\n showConfirmButton: true,\r\n closeOnConfirm: false,\r\n showLoaderOnConfirm: true,\r\n inputValue: `${currentPath}${currentName}`,\r\n }, (val) => {\r\n if (val === false) {\r\n return false;\r\n }\r\n\r\n $.ajax({\r\n type: 'POST',\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/copy`,\r\n timeout: 10000,\r\n data: JSON.stringify({\r\n from: `${currentPath}${currentName}`,\r\n to: `${val}`,\r\n }),\r\n }).done(data => {\r\n swal({\r\n type: 'success',\r\n title: '',\r\n text: 'File successfully copied.'\r\n });\r\n Files.list();\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n var error = 'An error occurred while trying to process this request.';\r\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\r\n error = jqXHR.responseJSON.error;\r\n }\r\n swal({\r\n type: 'error',\r\n title: '',\r\n text: error,\r\n });\r\n });\r\n });\r\n }\r\n\r\n download() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const fileName = decodeURIComponent(nameBlock.attr('data-name'));\r\n const filePath = decodeURIComponent(nameBlock.data('path'));\r\n\r\n window.location = `/server/${Pterodactyl.server.uuidShort}/files/download/${filePath}${fileName}`;\r\n }\r\n\r\n delete() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const delPath = decodeURIComponent(nameBlock.data('path'));\r\n const delName = decodeURIComponent(nameBlock.data('name'));\r\n\r\n swal({\r\n type: 'warning',\r\n title: '',\r\n text: 'Are you sure you want to delete ' + this.sanitizedString(delName) + '?',\r\n html: true,\r\n showCancelButton: true,\r\n showConfirmButton: true,\r\n closeOnConfirm: false,\r\n showLoaderOnConfirm: true\r\n }, () => {\r\n $.ajax({\r\n type: 'POST',\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`,\r\n timeout: 10000,\r\n data: JSON.stringify({\r\n items: [`${delPath}${delName}`]\r\n }),\r\n }).done(data => {\r\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\r\n swal({\r\n type: 'success',\r\n title: 'File Deleted'\r\n });\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n swal({\r\n type: 'error',\r\n title: 'Whoops!',\r\n html: true,\r\n text: 'An error occurred while attempting to delete this file. Please try again.',\r\n });\r\n });\r\n });\r\n }\r\n\r\n toggleMassActions() {\r\n if ($('#file_listing input[type=\"checkbox\"]:checked').length) {\r\n $('#mass_actions').removeClass('disabled');\r\n } else {\r\n $('#mass_actions').addClass('disabled');\r\n }\r\n }\r\n\r\n toggleHighlight(event) {\r\n const parent = $(event.currentTarget);\r\n const item = $(event.currentTarget).find('input');\r\n\r\n if($(item).is(':checked')) {\r\n $(item).prop('checked', false);\r\n parent.removeClass('warning').delay(200);\r\n } else {\r\n $(item).prop('checked', true);\r\n parent.addClass('warning').delay(200);\r\n }\r\n }\r\n\r\n highlightAll(event) {\r\n let parent;\r\n const item = $(event.currentTarget).find('input');\r\n\r\n if($(item).is(':checked')) {\r\n $('#file_listing input[type=checkbox]').prop('checked', false);\r\n $('#file_listing input[data-action=\"addSelection\"]').each(function() {\r\n parent = $(this).closest('tr');\r\n parent.removeClass('warning').delay(200);\r\n });\r\n } else {\r\n $('#file_listing input[type=checkbox]').prop('checked', true);\r\n $('#file_listing input[data-action=\"addSelection\"]').each(function() {\r\n parent = $(this).closest('tr');\r\n parent.addClass('warning').delay(200);\r\n });\r\n }\r\n }\r\n\r\n deleteSelected() {\r\n let selectedItems = [];\r\n let selectedItemsElements = [];\r\n let parent;\r\n let nameBlock;\r\n let delLocation;\r\n\r\n $('#file_listing input[data-action=\"addSelection\"]:checked').each(function() {\r\n parent = $(this).closest('tr');\r\n nameBlock = $(parent).find('td[data-identifier=\"name\"]');\r\n delLocation = decodeURIComponent(nameBlock.data('path')) + decodeURIComponent(nameBlock.data('name'));\r\n\r\n selectedItems.push(delLocation);\r\n selectedItemsElements.push(parent);\r\n });\r\n\r\n if (selectedItems.length != 0)\r\n {\r\n let formattedItems = \"\";\r\n let i = 0;\r\n let self = this;\r\n\r\n $.each(selectedItems, function(key, value) {\r\n formattedItems += (\"\" + self.sanitizedString(value) + \", \");\r\n i++;\r\n return i < 5;\r\n });\r\n\r\n formattedItems = formattedItems.slice(0, -2);\r\n if (selectedItems.length > 5) {\r\n formattedItems += ', and ' + (selectedItems.length - 5) + ' other(s)';\r\n }\r\n\r\n swal({\r\n type: 'warning',\r\n title: '',\r\n text: 'Are you sure you want to delete the following files: ' + formattedItems + '?',\r\n html: true,\r\n showCancelButton: true,\r\n showConfirmButton: true,\r\n closeOnConfirm: false,\r\n showLoaderOnConfirm: true\r\n }, () => {\r\n $.ajax({\r\n type: 'POST',\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`,\r\n timeout: 10000,\r\n data: JSON.stringify({\r\n items: selectedItems\r\n }),\r\n }).done(data => {\r\n $('#file_listing input:checked').each(function() {\r\n $(this).prop('checked', false);\r\n });\r\n\r\n $.each(selectedItemsElements, function() {\r\n $(this).addClass('warning').delay(200).fadeOut();\r\n })\r\n\r\n swal({\r\n type: 'success',\r\n title: 'Files Deleted'\r\n });\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n swal({\r\n type: 'error',\r\n title: 'Whoops!',\r\n html: true,\r\n text: 'An error occurred while attempting to delete these files. Please try again.',\r\n });\r\n });\r\n });\r\n } else {\r\n swal({\r\n type: 'warning',\r\n title: '',\r\n text: 'Please select files/folders to delete.',\r\n });\r\n }\r\n }\r\n\r\n decompress() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const compPath = decodeURIComponent(nameBlock.data('path'));\r\n const compName = decodeURIComponent(nameBlock.data('name'));\r\n\r\n swal({\r\n title: ' Decompressing...',\r\n text: 'This might take a few seconds to complete.',\r\n html: true,\r\n allowOutsideClick: false,\r\n allowEscapeKey: false,\r\n showConfirmButton: false,\r\n });\r\n\r\n $.ajax({\r\n type: 'POST',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/decompress`,\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify({\r\n files: `${compPath}${compName}`\r\n })\r\n }).done(data => {\r\n swal.close();\r\n Files.list(compPath);\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n var error = 'An error occurred while trying to process this request.';\r\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\r\n error = jqXHR.responseJSON.error;\r\n }\r\n swal({\r\n type: 'error',\r\n title: 'Whoops!',\r\n html: true,\r\n text: error\r\n });\r\n });\r\n }\r\n\r\n compress() {\r\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\r\n const compPath = decodeURIComponent(nameBlock.data('path'));\r\n const compName = decodeURIComponent(nameBlock.data('name'));\r\n\r\n $.ajax({\r\n type: 'POST',\r\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/compress`,\r\n headers: {\r\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\r\n 'X-Access-Server': Pterodactyl.server.uuid,\r\n },\r\n contentType: 'application/json; charset=utf-8',\r\n data: JSON.stringify({\r\n files: `${compPath}${compName}`,\r\n to: compPath.toString()\r\n })\r\n }).done(data => {\r\n Files.list(compPath, err => {\r\n if (err) return;\r\n const fileListing = $('#file_listing').find(`[data-name=\"${data.saved_as}\"]`).parent();\r\n fileListing.addClass('success pulsate').delay(3000).queue(() => {\r\n fileListing.removeClass('success pulsate').dequeue();\r\n });\r\n });\r\n }).fail(jqXHR => {\r\n console.error(jqXHR);\r\n var error = 'An error occurred while trying to process this request.';\r\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\r\n error = jqXHR.responseJSON.error;\r\n }\r\n swal({\r\n type: 'error',\r\n title: 'Whoops!',\r\n html: true,\r\n text: this.sanitizedString(error)\r\n });\r\n });\r\n }\r\n}\r\n","\"use strict\";\r\n\r\n// Copyright (c) 2015 - 2017 Dane Everitt \r\n//\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy\r\n// of this software and associated documentation files (the \"Software\"), to deal\r\n// in the Software without restriction, including without limitation the rights\r\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n// copies of the Software, and to permit persons to whom the Software is\r\n// furnished to do so, subject to the following conditions:\r\n//\r\n// The above copyright notice and this permission notice shall be included in all\r\n// copies or substantial portions of the Software.\r\n//\r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n// SOFTWARE.\r\nclass ContextMenuClass {\r\n constructor() {\r\n this.activeLine = null;\r\n }\r\n\r\n run() {\r\n this.directoryClick();\r\n this.rightClick();\r\n }\r\n\r\n makeMenu(parent) {\r\n $(document).find('#fileOptionMenu').remove();\r\n if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\r\n\r\n let newFilePath = $('#file_listing').data('current-dir');\r\n if (parent.data('type') === 'folder') {\r\n const nameBlock = parent.find('td[data-identifier=\"name\"]');\r\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\r\n const currentPath = decodeURIComponent(nameBlock.data('path'));\r\n newFilePath = `${currentPath}${currentName}`;\r\n }\r\n\r\n let buildMenu = '
      ';\r\n\r\n if (Pterodactyl.permissions.moveFiles) {\r\n buildMenu += '
    • Rename
    • \\\r\n
    • Move
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.copyFiles) {\r\n buildMenu += '
    • Copy
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.compressFiles) {\r\n buildMenu += '
    • Compress
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.decompressFiles) {\r\n buildMenu += '
    • Decompress
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.createFiles) {\r\n buildMenu += '
    • \\\r\n
    • ').text(newFilePath).html() + '\" class=\"text-muted\"> New File
    • \\\r\n
    • New Folder
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.downloadFiles || Pterodactyl.permissions.deleteFiles) {\r\n buildMenu += '
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.downloadFiles) {\r\n buildMenu += '
    • Download
    • ';\r\n }\r\n\r\n if (Pterodactyl.permissions.deleteFiles) {\r\n buildMenu += '
    • Delete
    • ';\r\n }\r\n\r\n buildMenu += '
    ';\r\n return buildMenu;\r\n }\r\n\r\n rightClick() {\r\n $('[data-action=\"toggleMenu\"]').on('mousedown', event => {\r\n event.preventDefault();\r\n if ($(document).find('#fileOptionMenu').is(':visible')) {\r\n $('body').trigger('click');\r\n return;\r\n }\r\n this.showMenu(event);\r\n });\r\n $('#file_listing > tbody td').on('contextmenu', event => {\r\n this.showMenu(event);\r\n });\r\n }\r\n\r\n showMenu(event) {\r\n const parent = $(event.target).closest('tr');\r\n const menu = $(this.makeMenu(parent));\r\n\r\n if (parent.data('type') === 'disabled') return;\r\n event.preventDefault();\r\n\r\n $(menu).appendTo('body');\r\n $(menu).data('invokedOn', $(event.target)).show().css({\r\n position: 'absolute',\r\n left: event.pageX - 150,\r\n top: event.pageY,\r\n });\r\n\r\n this.activeLine = parent;\r\n this.activeLine.addClass('active');\r\n\r\n // Handle Events\r\n const Actions = new ActionsClass(parent, menu);\r\n if (Pterodactyl.permissions.moveFiles) {\r\n $(menu).find('li[data-action=\"move\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.move();\r\n });\r\n $(menu).find('li[data-action=\"rename\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.rename();\r\n });\r\n }\r\n\r\n if (Pterodactyl.permissions.copyFiles) {\r\n $(menu).find('li[data-action=\"copy\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.copy();\r\n });\r\n }\r\n\r\n if (Pterodactyl.permissions.compressFiles) {\r\n if (parent.data('type') === 'folder') {\r\n $(menu).find('li[data-action=\"compress\"]').removeClass('hidden');\r\n }\r\n $(menu).find('li[data-action=\"compress\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.compress();\r\n });\r\n }\r\n\r\n if (Pterodactyl.permissions.decompressFiles) {\r\n if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) {\r\n $(menu).find('li[data-action=\"decompress\"]').removeClass('hidden');\r\n }\r\n $(menu).find('li[data-action=\"decompress\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.decompress();\r\n });\r\n }\r\n\r\n if (Pterodactyl.permissions.createFiles) {\r\n $(menu).find('li[data-action=\"folder\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.folder();\r\n });\r\n }\r\n\r\n if (Pterodactyl.permissions.downloadFiles) {\r\n if (parent.data('type') === 'file') {\r\n $(menu).find('li[data-action=\"download\"]').removeClass('hidden');\r\n }\r\n $(menu).find('li[data-action=\"download\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.download();\r\n });\r\n }\r\n\r\n if (Pterodactyl.permissions.deleteFiles) {\r\n $(menu).find('li[data-action=\"delete\"]').unbind().on('click', e => {\r\n e.preventDefault();\r\n Actions.delete();\r\n });\r\n }\r\n\r\n $(window).unbind().on('click', event => {\r\n if($(event.target).is('.disable-menu-hide')) {\r\n event.preventDefault();\r\n return;\r\n }\r\n $(menu).unbind().remove();\r\n if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\r\n });\r\n }\r\n\r\n directoryClick() {\r\n $('a[data-action=\"directory-view\"]').on('click', function (event) {\r\n event.preventDefault();\r\n\r\n const path = $(this).parent().data('path') || '';\r\n const name = $(this).parent().data('name') || '';\r\n\r\n window.location.hash = encodeURIComponent(path + name);\r\n Files.list();\r\n });\r\n }\r\n}\r\n\r\nwindow.ContextMenu = new ContextMenuClass;\r\n","\"use strict\";\r\n\r\n// Copyright (c) 2015 - 2017 Dane Everitt \r\n//\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy\r\n// of this software and associated documentation files (the \"Software\"), to deal\r\n// in the Software without restriction, including without limitation the rights\r\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n// copies of the Software, and to permit persons to whom the Software is\r\n// furnished to do so, subject to the following conditions:\r\n//\r\n// The above copyright notice and this permission notice shall be included in all\r\n// copies or substantial portions of the Software.\r\n//\r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n// SOFTWARE.\r\nclass FileManager {\r\n constructor() {\r\n this.list(this.decodeHash());\r\n }\r\n\r\n list(path, next) {\r\n if (_.isUndefined(path)) {\r\n path = this.decodeHash();\r\n }\r\n\r\n this.loader(true);\r\n $.ajax({\r\n type: 'POST',\r\n url: Pterodactyl.meta.directoryList,\r\n headers: {\r\n 'X-CSRF-Token': Pterodactyl.meta.csrftoken,\r\n },\r\n data: {\r\n directory: path,\r\n },\r\n }).done(data => {\r\n this.loader(false);\r\n $('#load_files').slideUp(10).html(data).slideDown(10, () => {\r\n ContextMenu.run();\r\n this.reloadFilesButton();\r\n this.addFolderButton();\r\n this.selectItem();\r\n this.selectAll();\r\n this.selectiveDeletion();\r\n this.selectRow();\r\n if (_.isFunction(next)) {\r\n return next();\r\n }\r\n });\r\n $('#internal_alert').slideUp();\r\n\r\n if (typeof Siofu === 'object') {\r\n Siofu.listenOnInput(document.getElementById(\"files_touch_target\"));\r\n }\r\n }).fail(jqXHR => {\r\n this.loader(false);\r\n if (_.isFunction(next)) {\r\n return next(new Error('Failed to load file listing.'));\r\n }\r\n\r\n if ((path !== '' && path !== '/') && jqXHR.status === 404) {\r\n return this.list('', next);\r\n }\r\n\r\n swal({\r\n type: 'error',\r\n title: 'File Error',\r\n text: jqXHR.responseJSON.errors[0].detail || 'An error occurred while attempting to process this request. Please try again.',\r\n });\r\n console.error(jqXHR);\r\n });\r\n }\r\n\r\n loader(show) {\r\n if (show){\r\n $('.file-overlay').fadeIn(100);\r\n } else {\r\n $('.file-overlay').fadeOut(100);\r\n }\r\n }\r\n\r\n reloadFilesButton() {\r\n $('i[data-action=\"reload-files\"]').unbind().on('click', () => {\r\n $('i[data-action=\"reload-files\"]').addClass('fa-spin');\r\n this.list();\r\n });\r\n }\r\n\r\n selectItem() {\r\n $('[data-action=\"addSelection\"]').on('click', event => {\r\n event.preventDefault();\r\n });\r\n }\r\n\r\n selectAll() {\r\n $('[data-action=\"selectAll\"]').on('click', event => {\r\n event.preventDefault();\r\n });\r\n }\r\n\r\n selectiveDeletion() {\r\n $('[data-action=\"selective-deletion\"]').on('mousedown', event => {\r\n new ActionsClass().deleteSelected();\r\n });\r\n }\r\n\r\n addFolderButton() {\r\n $('[data-action=\"add-folder\"]').unbind().on('click', () => {\r\n new ActionsClass().folder($('#file_listing').data('current-dir') || '/');\r\n });\r\n }\r\n\r\n selectRow() {\r\n $('#file_listing tr').on('mousedown', event => {\r\n if (event.which === 1) {\r\n if ($(event.target).is('th') || $(event.target).is('input[data-action=\"selectAll\"]')) {\r\n new ActionsClass().highlightAll(event);\r\n } else if ($(event.target).is('td') || $(event.target).is('input[data-action=\"addSelection\"]')) {\r\n new ActionsClass().toggleHighlight(event);\r\n }\r\n\r\n new ActionsClass().toggleMassActions();\r\n }\r\n });\r\n }\r\n\r\n decodeHash() {\r\n return decodeURIComponent(window.location.hash.substring(1));\r\n }\r\n\r\n}\r\n\r\nwindow.Files = new FileManager;\r\n"]} \ No newline at end of file diff --git a/public/themes/pterodactyl/js/frontend/files/src/actions.js b/public/themes/pterodactyl/js/frontend/files/src/actions.js deleted file mode 100644 index 244bcaab6..000000000 --- a/public/themes/pterodactyl/js/frontend/files/src/actions.js +++ /dev/null @@ -1,549 +0,0 @@ -"use strict"; - -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -class ActionsClass { - constructor(element, menu) { - this.element = element; - this.menu = menu; - } - - destroy() { - this.element = undefined; - } - - sanitizedString(value) { - return $('
    ').text(value).html(); - } - - folder(path) { - let inputValue - if (path) { - inputValue = path - } else { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.data('name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - if ($(this.element).data('type') === 'file') { - inputValue = currentPath; - } else { - inputValue = `${currentPath}${currentName}/`; - } - } - - swal({ - type: 'input', - title: 'Create Folder', - text: 'Please enter the path and folder name below.', - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true, - inputValue: inputValue - }, (val) => { - if (val === false) { - return false; - } - - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/folder`, - timeout: 10000, - data: JSON.stringify({ - path: val, - }), - }).done(data => { - swal.close(); - Files.list(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occurred while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: '', - text: error, - }); - }); - }); - } - - move() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - swal({ - type: 'input', - title: 'Move File', - text: 'Please enter the new path for the file below.', - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true, - inputValue: `${currentPath}${currentName}`, - }, (val) => { - if (val === false) { - return false; - } - - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/move`, - timeout: 10000, - data: JSON.stringify({ - from: `${currentPath}${currentName}`, - to: `${val}`, - }), - }).done(data => { - nameBlock.parent().addClass('warning').delay(200).fadeOut(); - swal.close(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occurred while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: '', - text: error, - }); - }); - }); - - } - - rename() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentLink = nameBlock.find('a'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const attachEditor = ` - - - `; - - nameBlock.html(attachEditor); - const inputField = nameBlock.find('input'); - const inputLoader = nameBlock.find('.input-loader'); - - inputField.focus(); - inputField.on('blur keydown', e => { - // Save Field - if ( - (e.type === 'keydown' && e.which === 27) - || e.type === 'blur' - || (e.type === 'keydown' && e.which === 13 && currentName === inputField.val()) - ) { - if (!_.isEmpty(currentLink)) { - nameBlock.html(currentLink); - } else { - nameBlock.html(currentName); - } - inputField.remove(); - ContextMenu.unbind().run(); - return; - } - - if (e.type === 'keydown' && e.which !== 13) return; - - inputLoader.show(); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/rename`, - timeout: 10000, - data: JSON.stringify({ - from: `${currentPath}${currentName}`, - to: `${currentPath}${inputField.val()}`, - }), - }).done(data => { - nameBlock.attr('data-name', inputField.val()); - if (!_.isEmpty(currentLink)) { - let newLink = currentLink.attr('href'); - if (nameBlock.parent().data('type') !== 'folder') { - newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val(); - } - currentLink.attr('href', newLink); - nameBlock.html( - currentLink.html(inputField.val()) - ); - } else { - nameBlock.html(inputField.val()); - } - inputField.remove(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occurred while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - nameBlock.addClass('has-error').delay(2000).queue(() => { - nameBlock.removeClass('has-error').dequeue(); - }); - inputField.popover({ - animation: true, - placement: 'top', - content: error, - title: 'Save Error' - }).popover('show'); - }).always(() => { - inputLoader.remove(); - ContextMenu.unbind().run(); - }); - }); - } - - copy() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - - swal({ - type: 'input', - title: 'Copy File', - text: 'Please enter the new path for the copied file below.', - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true, - inputValue: `${currentPath}${currentName}`, - }, (val) => { - if (val === false) { - return false; - } - - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/copy`, - timeout: 10000, - data: JSON.stringify({ - from: `${currentPath}${currentName}`, - to: `${val}`, - }), - }).done(data => { - swal({ - type: 'success', - title: '', - text: 'File successfully copied.' - }); - Files.list(); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occurred while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: '', - text: error, - }); - }); - }); - } - - download() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const fileName = decodeURIComponent(nameBlock.attr('data-name')); - const filePath = decodeURIComponent(nameBlock.data('path')); - - window.location = `/server/${Pterodactyl.server.uuidShort}/files/download/${filePath}${fileName}`; - } - - delete() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const delPath = decodeURIComponent(nameBlock.data('path')); - const delName = decodeURIComponent(nameBlock.data('name')); - - swal({ - type: 'warning', - title: '', - text: 'Are you sure you want to delete ' + this.sanitizedString(delName) + '?', - html: true, - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true - }, () => { - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`, - timeout: 10000, - data: JSON.stringify({ - items: [`${delPath}${delName}`] - }), - }).done(data => { - nameBlock.parent().addClass('warning').delay(200).fadeOut(); - swal({ - type: 'success', - title: 'File Deleted' - }); - }).fail(jqXHR => { - console.error(jqXHR); - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: 'An error occurred while attempting to delete this file. Please try again.', - }); - }); - }); - } - - toggleMassActions() { - if ($('#file_listing input[type="checkbox"]:checked').length) { - $('#mass_actions').removeClass('disabled'); - } else { - $('#mass_actions').addClass('disabled'); - } - } - - toggleHighlight(event) { - const parent = $(event.currentTarget); - const item = $(event.currentTarget).find('input'); - - if($(item).is(':checked')) { - $(item).prop('checked', false); - parent.removeClass('warning').delay(200); - } else { - $(item).prop('checked', true); - parent.addClass('warning').delay(200); - } - } - - highlightAll(event) { - let parent; - const item = $(event.currentTarget).find('input'); - - if($(item).is(':checked')) { - $('#file_listing input[type=checkbox]').prop('checked', false); - $('#file_listing input[data-action="addSelection"]').each(function() { - parent = $(this).closest('tr'); - parent.removeClass('warning').delay(200); - }); - } else { - $('#file_listing input[type=checkbox]').prop('checked', true); - $('#file_listing input[data-action="addSelection"]').each(function() { - parent = $(this).closest('tr'); - parent.addClass('warning').delay(200); - }); - } - } - - deleteSelected() { - let selectedItems = []; - let selectedItemsElements = []; - let parent; - let nameBlock; - let delLocation; - - $('#file_listing input[data-action="addSelection"]:checked').each(function() { - parent = $(this).closest('tr'); - nameBlock = $(parent).find('td[data-identifier="name"]'); - delLocation = decodeURIComponent(nameBlock.data('path')) + decodeURIComponent(nameBlock.data('name')); - - selectedItems.push(delLocation); - selectedItemsElements.push(parent); - }); - - if (selectedItems.length != 0) - { - let formattedItems = ""; - let i = 0; - let self = this; - - $.each(selectedItems, function(key, value) { - formattedItems += ("" + self.sanitizedString(value) + ", "); - i++; - return i < 5; - }); - - formattedItems = formattedItems.slice(0, -2); - if (selectedItems.length > 5) { - formattedItems += ', and ' + (selectedItems.length - 5) + ' other(s)'; - } - - swal({ - type: 'warning', - title: '', - text: 'Are you sure you want to delete the following files: ' + formattedItems + '?', - html: true, - showCancelButton: true, - showConfirmButton: true, - closeOnConfirm: false, - showLoaderOnConfirm: true - }, () => { - $.ajax({ - type: 'POST', - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`, - timeout: 10000, - data: JSON.stringify({ - items: selectedItems - }), - }).done(data => { - $('#file_listing input:checked').each(function() { - $(this).prop('checked', false); - }); - - $.each(selectedItemsElements, function() { - $(this).addClass('warning').delay(200).fadeOut(); - }) - - swal({ - type: 'success', - title: 'Files Deleted' - }); - }).fail(jqXHR => { - console.error(jqXHR); - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: 'An error occurred while attempting to delete these files. Please try again.', - }); - }); - }); - } else { - swal({ - type: 'warning', - title: '', - text: 'Please select files/folders to delete.', - }); - } - } - - decompress() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const compPath = decodeURIComponent(nameBlock.data('path')); - const compName = decodeURIComponent(nameBlock.data('name')); - - swal({ - title: ' Decompressing...', - text: 'This might take a few seconds to complete.', - html: true, - allowOutsideClick: false, - allowEscapeKey: false, - showConfirmButton: false, - }); - - $.ajax({ - type: 'POST', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/decompress`, - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({ - files: `${compPath}${compName}` - }) - }).done(data => { - swal.close(); - Files.list(compPath); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occurred while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: error - }); - }); - } - - compress() { - const nameBlock = $(this.element).find('td[data-identifier="name"]'); - const compPath = decodeURIComponent(nameBlock.data('path')); - const compName = decodeURIComponent(nameBlock.data('name')); - - $.ajax({ - type: 'POST', - url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/compress`, - headers: { - 'X-Access-Token': Pterodactyl.server.daemonSecret, - 'X-Access-Server': Pterodactyl.server.uuid, - }, - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({ - files: `${compPath}${compName}`, - to: compPath.toString() - }) - }).done(data => { - Files.list(compPath, err => { - if (err) return; - const fileListing = $('#file_listing').find(`[data-name="${data.saved_as}"]`).parent(); - fileListing.addClass('success pulsate').delay(3000).queue(() => { - fileListing.removeClass('success pulsate').dequeue(); - }); - }); - }).fail(jqXHR => { - console.error(jqXHR); - var error = 'An error occurred while trying to process this request.'; - if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { - error = jqXHR.responseJSON.error; - } - swal({ - type: 'error', - title: 'Whoops!', - html: true, - text: this.sanitizedString(error) - }); - }); - } -} diff --git a/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js b/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js deleted file mode 100644 index 6796f8b09..000000000 --- a/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js +++ /dev/null @@ -1,203 +0,0 @@ -"use strict"; - -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -class ContextMenuClass { - constructor() { - this.activeLine = null; - } - - run() { - this.directoryClick(); - this.rightClick(); - } - - makeMenu(parent) { - $(document).find('#fileOptionMenu').remove(); - if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active'); - - let newFilePath = $('#file_listing').data('current-dir'); - if (parent.data('type') === 'folder') { - const nameBlock = parent.find('td[data-identifier="name"]'); - const currentName = decodeURIComponent(nameBlock.attr('data-name')); - const currentPath = decodeURIComponent(nameBlock.data('path')); - newFilePath = `${currentPath}${currentName}`; - } - - let buildMenu = ''; - return buildMenu; - } - - rightClick() { - $('[data-action="toggleMenu"]').on('mousedown', event => { - event.preventDefault(); - if ($(document).find('#fileOptionMenu').is(':visible')) { - $('body').trigger('click'); - return; - } - this.showMenu(event); - }); - $('#file_listing > tbody td').on('contextmenu', event => { - this.showMenu(event); - }); - } - - showMenu(event) { - const parent = $(event.target).closest('tr'); - const menu = $(this.makeMenu(parent)); - - if (parent.data('type') === 'disabled') return; - event.preventDefault(); - - $(menu).appendTo('body'); - $(menu).data('invokedOn', $(event.target)).show().css({ - position: 'absolute', - left: event.pageX - 150, - top: event.pageY, - }); - - this.activeLine = parent; - this.activeLine.addClass('active'); - - // Handle Events - const Actions = new ActionsClass(parent, menu); - if (Pterodactyl.permissions.moveFiles) { - $(menu).find('li[data-action="move"]').unbind().on('click', e => { - e.preventDefault(); - Actions.move(); - }); - $(menu).find('li[data-action="rename"]').unbind().on('click', e => { - e.preventDefault(); - Actions.rename(); - }); - } - - if (Pterodactyl.permissions.copyFiles) { - $(menu).find('li[data-action="copy"]').unbind().on('click', e => { - e.preventDefault(); - Actions.copy(); - }); - } - - if (Pterodactyl.permissions.compressFiles) { - if (parent.data('type') === 'folder') { - $(menu).find('li[data-action="compress"]').removeClass('hidden'); - } - $(menu).find('li[data-action="compress"]').unbind().on('click', e => { - e.preventDefault(); - Actions.compress(); - }); - } - - if (Pterodactyl.permissions.decompressFiles) { - if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) { - $(menu).find('li[data-action="decompress"]').removeClass('hidden'); - } - $(menu).find('li[data-action="decompress"]').unbind().on('click', e => { - e.preventDefault(); - Actions.decompress(); - }); - } - - if (Pterodactyl.permissions.createFiles) { - $(menu).find('li[data-action="folder"]').unbind().on('click', e => { - e.preventDefault(); - Actions.folder(); - }); - } - - if (Pterodactyl.permissions.downloadFiles) { - if (parent.data('type') === 'file') { - $(menu).find('li[data-action="download"]').removeClass('hidden'); - } - $(menu).find('li[data-action="download"]').unbind().on('click', e => { - e.preventDefault(); - Actions.download(); - }); - } - - if (Pterodactyl.permissions.deleteFiles) { - $(menu).find('li[data-action="delete"]').unbind().on('click', e => { - e.preventDefault(); - Actions.delete(); - }); - } - - $(window).unbind().on('click', event => { - if($(event.target).is('.disable-menu-hide')) { - event.preventDefault(); - return; - } - $(menu).unbind().remove(); - if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active'); - }); - } - - directoryClick() { - $('a[data-action="directory-view"]').on('click', function (event) { - event.preventDefault(); - - const path = $(this).parent().data('path') || ''; - const name = $(this).parent().data('name') || ''; - - window.location.hash = encodeURIComponent(path + name); - Files.list(); - }); - } -} - -window.ContextMenu = new ContextMenuClass; diff --git a/public/themes/pterodactyl/js/frontend/files/src/index.js b/public/themes/pterodactyl/js/frontend/files/src/index.js deleted file mode 100644 index 83c1460bb..000000000 --- a/public/themes/pterodactyl/js/frontend/files/src/index.js +++ /dev/null @@ -1,139 +0,0 @@ -"use strict"; - -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -class FileManager { - constructor() { - this.list(this.decodeHash()); - } - - list(path, next) { - if (_.isUndefined(path)) { - path = this.decodeHash(); - } - - this.loader(true); - $.ajax({ - type: 'POST', - url: Pterodactyl.meta.directoryList, - headers: { - 'X-CSRF-Token': Pterodactyl.meta.csrftoken, - }, - data: { - directory: path, - }, - }).done(data => { - this.loader(false); - $('#load_files').slideUp(10).html(data).slideDown(10, () => { - ContextMenu.run(); - this.reloadFilesButton(); - this.addFolderButton(); - this.selectItem(); - this.selectAll(); - this.selectiveDeletion(); - this.selectRow(); - if (_.isFunction(next)) { - return next(); - } - }); - $('#internal_alert').slideUp(); - - if (typeof Siofu === 'object') { - Siofu.listenOnInput(document.getElementById("files_touch_target")); - } - }).fail(jqXHR => { - this.loader(false); - if (_.isFunction(next)) { - return next(new Error('Failed to load file listing.')); - } - - if ((path !== '' && path !== '/') && jqXHR.status === 404) { - return this.list('', next); - } - - swal({ - type: 'error', - title: 'File Error', - text: jqXHR.responseJSON.errors[0].detail || 'An error occurred while attempting to process this request. Please try again.', - }); - console.error(jqXHR); - }); - } - - loader(show) { - if (show){ - $('.file-overlay').fadeIn(100); - } else { - $('.file-overlay').fadeOut(100); - } - } - - reloadFilesButton() { - $('i[data-action="reload-files"]').unbind().on('click', () => { - $('i[data-action="reload-files"]').addClass('fa-spin'); - this.list(); - }); - } - - selectItem() { - $('[data-action="addSelection"]').on('click', event => { - event.preventDefault(); - }); - } - - selectAll() { - $('[data-action="selectAll"]').on('click', event => { - event.preventDefault(); - }); - } - - selectiveDeletion() { - $('[data-action="selective-deletion"]').on('mousedown', event => { - new ActionsClass().deleteSelected(); - }); - } - - addFolderButton() { - $('[data-action="add-folder"]').unbind().on('click', () => { - new ActionsClass().folder($('#file_listing').data('current-dir') || '/'); - }); - } - - selectRow() { - $('#file_listing tr').on('mousedown', event => { - if (event.which === 1) { - if ($(event.target).is('th') || $(event.target).is('input[data-action="selectAll"]')) { - new ActionsClass().highlightAll(event); - } else if ($(event.target).is('td') || $(event.target).is('input[data-action="addSelection"]')) { - new ActionsClass().toggleHighlight(event); - } - - new ActionsClass().toggleMassActions(); - } - }); - } - - decodeHash() { - return decodeURIComponent(window.location.hash.substring(1)); - } - -} - -window.Files = new FileManager; diff --git a/public/themes/pterodactyl/js/frontend/files/upload.js b/public/themes/pterodactyl/js/frontend/files/upload.js deleted file mode 100644 index 873fbf636..000000000 --- a/public/themes/pterodactyl/js/frontend/files/upload.js +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -(function initUploader() { - var notifyUploadSocketError = false; - uploadSocket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/v1/upload/' + Pterodactyl.server.uuid, { - 'query': 'token=' + Pterodactyl.server.daemonSecret, - }); - - uploadSocket.io.on('connect_error', function (err) { - if(typeof notifyUploadSocketError !== 'object') { - notifyUploadSocketError = $.notify({ - message: 'There was an error attempting to establish a connection to the uploader endpoint.

    ' + err, - }, { - type: 'danger', - delay: 0 - }); - } - }); - - uploadSocket.on('error', err => { - Siofu.destroy(); - console.error(err); - }); - - uploadSocket.on('connect', function () { - if (notifyUploadSocketError !== false) { - notifyUploadSocketError.close(); - notifyUploadSocketError = false; - } - }); - - window.Siofu = new SocketIOFileUpload(uploadSocket); - Siofu.listenOnDrop(document.getElementById("load_files")); - - if (document.getElementById("files_touch_target")) { - Siofu.listenOnInput(document.getElementById("files_touch_target")); - } - - window.addEventListener('dragover', function (event) { - event.preventDefault(); - }, false); - - window.addEventListener('drop', function (event) { - event.preventDefault(); - }, false); - - window.foldersDetectedInDrag = function (event) { - var folderDetected = false; - var files = event.dataTransfer.files; - for (var i = 0, f; f = files[i]; i++) { - if (!f.type && f.size === 0) { - return true; - } - } - - return folderDetected; - }; - - var dropCounter = 0; - $('#load_files').bind({ - dragenter: function (event) { - event.preventDefault(); - dropCounter++; - $(this).addClass('hasFileHover'); - }, - dragleave: function (event) { - dropCounter--; - if (dropCounter === 0) { - $(this).removeClass('hasFileHover'); - } - }, - drop: function (event) { - if (window.foldersDetectedInDrag(event.originalEvent)) { - $.notify({ - message: 'Folder uploads are not supported. Please use SFTP to upload whole directories.', - }, { - type: 'warning', - delay: 0 - }); - } - - dropCounter = 0; - $(this).removeClass('hasFileHover'); - } - }); - - Siofu.addEventListener('start', function (event) { - window.onbeforeunload = function () { - return 'A file upload in in progress, are you sure you want to continue?'; - }; - event.file.meta.path = $('#file_listing').data('current-dir'); - event.file.meta.identifier = Math.random().toString(36).slice(2); - - $('#append_files_to').append(' \ - \ - ' + event.file.name + ' \ -   \ - \ - \ -
    \ -
    \ -
    \ - \ - \ - '); - }); - - Siofu.addEventListener('progress', function(event) { - window.onbeforeunload = function () { - return 'A file upload in in progress, are you sure you want to continue?'; - }; - var percent = event.bytesLoaded / event.file.size * 100; - if (percent >= 100) { - $('.prog-bar-' + event.file.meta.identifier).css('width', '100%').removeClass('progress-bar-info').addClass('progress-bar-success').parent().removeClass('active'); - } else { - $('.prog-bar-' + event.file.meta.identifier).css('width', percent + '%'); - } - }); - - // Do something when a file is uploaded: - Siofu.addEventListener('complete', function(event) { - window.onbeforeunload = function () {}; - if (!event.success) { - $('.prog-bar-' + event.file.meta.identifier).css('width', '100%').removeClass('progress-bar-info').addClass('progress-bar-danger'); - $.notify({ - message: 'An error was encountered while attempting to upload this file.' - }, { - type: 'danger', - delay: 5000 - }); - } - }); - - Siofu.addEventListener('error', function(event) { - window.onbeforeunload = function () {}; - console.error(event); - $('.prog-bar-' + event.file.meta.identifier).css('width', '100%').removeClass('progress-bar-info').addClass('progress-bar-danger'); - $.notify({ - message: 'An error was encountered while attempting to upload this file: ' + event.message + '.', - }, { - type: 'danger', - delay: 8000 - }); - }); -})(); diff --git a/public/themes/pterodactyl/js/frontend/server.socket.js b/public/themes/pterodactyl/js/frontend/server.socket.js deleted file mode 100644 index 09063351d..000000000 --- a/public/themes/pterodactyl/js/frontend/server.socket.js +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -$('#console-popout').on('click', function (event) { - event.preventDefault(); - window.open($(this).attr('href'), 'Pterodactyl Console', 'width=800,height=400'); -}); -var Server = (function () { - - function initSocket() { - if (typeof $.notifyDefaults !== 'function') { - console.error('Notify does not appear to be loaded.'); - return; - } - - if (typeof io !== 'function') { - console.error('Socket.io is required to use this panel.'); - return; - } - - $.notifyDefaults({ - placement: { - from: 'bottom', - align: 'right' - }, - newest_on_top: true, - delay: 2000, - offset: { - x: 20, - y: 60, - }, - animate: { - enter: 'animated bounceInUp', - exit: 'animated bounceOutDown' - } - }); - - var notifySocketError = false; - - window.Socket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/v1/ws/' + Pterodactyl.server.uuid, { - 'query': 'token=' + Pterodactyl.server.daemonSecret, - }); - - Socket.on('error', function (err) { - if(typeof notifySocketError !== 'object') { - notifySocketError = $.notify({ - message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.

    ' + err, - }, { - type: 'danger', - delay: 0, - }); - } - setStatusIcon(999); - }); - - Socket.io.on('connect_error', function (err) { - if(typeof notifySocketError !== 'object') { - notifySocketError = $.notify({ - message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.

    ' + err, - }, { - type: 'danger', - delay: 0, - }); - } - setStatusIcon(999); - }); - - // Connected to Socket Successfully - Socket.on('connect', function () { - if (notifySocketError !== false) { - notifySocketError.close(); - notifySocketError = false; - } - }); - - Socket.on('initial status', function (data) { - setStatusIcon(data.status); - }); - - Socket.on('status', function (data) { - setStatusIcon(data.status); - }); - } - - function setStatusIcon(status) { - switch (status) { - case 0: - $('#server_status_icon').html(' Offline'); - break; - case 1: - $('#server_status_icon').html(' Online'); - break; - case 2: - $('#server_status_icon').html(' Starting'); - break; - case 3: - $('#server_status_icon').html(' Stopping'); - break; - default: - $('#server_status_icon').html(' Connection Error'); - break; - } - } - - return { - init: function () { - initSocket(); - }, - - setStatusIcon: setStatusIcon, - } - -})(); - -Server.init(); diff --git a/public/themes/pterodactyl/js/frontend/serverlist.js b/public/themes/pterodactyl/js/frontend/serverlist.js deleted file mode 100644 index 6e4d5c4e4..000000000 --- a/public/themes/pterodactyl/js/frontend/serverlist.js +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -(function updateServerStatus() { - var Status = { - 0: 'Offline', - 1: 'Online', - 2: 'Starting', - 3: 'Stopping' - }; - $('.dynamic-update').each(function (index, data) { - var element = $(this); - var serverShortUUID = $(this).data('server'); - - $.ajax({ - type: 'GET', - url: Router.route('index.status', { server: serverShortUUID }), - timeout: 5000, - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - } - }).done(function (data) { - if (typeof data.status === 'undefined') { - element.find('[data-action="status"]').html('Error'); - return; - } - switch (data.status) { - case 0: - element.find('[data-action="status"]').html('Offline'); - break; - case 1: - element.find('[data-action="status"]').html('Online'); - break; - case 2: - element.find('[data-action="status"]').html('Starting'); - break; - case 3: - element.find('[data-action="status"]').html('Stopping'); - break; - case 20: - element.find('[data-action="status"]').html('Installing'); - break; - case 30: - element.find('[data-action="status"]').html('Suspended'); - break; - } - if (data.status > 0 && data.status < 4) { - var cpuMax = element.find('[data-action="cpu"]').data('cpumax'); - var currentCpu = data.proc.cpu.total; - if (cpuMax !== 0) { - currentCpu = parseFloat(((data.proc.cpu.total / cpuMax) * 100).toFixed(2).toString()); - } - if (data.status !== 0) { - var cpuMax = element.find('[data-action="cpu"]').data('cpumax'); - var currentCpu = data.proc.cpu.total; - if (cpuMax !== 0) { - currentCpu = parseFloat(((data.proc.cpu.total / cpuMax) * 100).toFixed(2).toString()); - } - element.find('[data-action="memory"]').html(parseInt(data.proc.memory.total / (1024 * 1024))); - element.find('[data-action="cpu"]').html(currentCpu); - element.find('[data-action="disk"]').html(parseInt(data.proc.disk.used)); - } else { - element.find('[data-action="memory"]').html('--'); - element.find('[data-action="cpu"]').html('--'); - element.find('[data-action="disk"]').html('--'); - } - } - }).fail(function (jqXHR) { - if (jqXHR.status === 504) { - element.find('[data-action="status"]').html('Gateway Timeout'); - } else { - element.find('[data-action="status"]').html('Error'); - } - }); - }).promise().done(function () { - setTimeout(updateServerStatus, 10000); - }); -})(); diff --git a/public/themes/pterodactyl/js/frontend/tasks/management-actions.js b/public/themes/pterodactyl/js/frontend/tasks/management-actions.js deleted file mode 100644 index 857c32cfa..000000000 --- a/public/themes/pterodactyl/js/frontend/tasks/management-actions.js +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. - -$(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-action="delete-schedule"]').click(function () { - var self = $(this); - swal({ - type: 'error', - title: 'Delete Schedule?', - text: 'Are you sure you want to delete this schedule? There is no undo.', - showCancelButton: true, - allowOutsideClick: true, - closeOnConfirm: false, - confirmButtonText: 'Delete Schedule', - confirmButtonColor: '#d9534f', - showLoaderOnConfirm: true - }, function () { - $.ajax({ - method: 'DELETE', - url: Router.route('server.schedules.view', { - server: Pterodactyl.server.uuidShort, - schedule: self.data('schedule-id'), - }), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - } - }).done(function (data) { - swal({ - type: 'success', - title: '', - text: 'Schedule has been deleted.' - }); - self.parent().parent().slideUp(); - }).fail(function (jqXHR) { - console.error(jqXHR); - swal({ - type: 'error', - title: 'Whoops!', - text: 'An error occurred while attempting to delete this schedule.' - }); - }); - }); - }); - - $('[data-action="trigger-schedule"]').click(function (event) { - event.preventDefault(); - var self = $(this); - swal({ - type: 'info', - title: 'Trigger Schedule', - text: 'This will run the selected schedule now.', - showCancelButton: true, - allowOutsideClick: true, - closeOnConfirm: false, - confirmButtonText: 'Continue', - showLoaderOnConfirm: true - }, function () { - $.ajax({ - method: 'POST', - url: Router.route('server.schedules.trigger', { - server: Pterodactyl.server.uuidShort, - schedule: self.data('schedule-id'), - }), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - }, - }).done(function (data) { - swal({ - type: 'success', - title: '', - text: 'Schedule has been added to the next-run queue.' - }); - }).fail(function (jqXHR) { - console.error(jqXHR); - swal({ - type: 'error', - title: 'Whoops!', - text: 'An error occurred while attempting to trigger this schedule.' - }); - }); - }); - }); - - $('[data-action="toggle-schedule"]').click(function (event) { - var self = $(this); - swal({ - type: 'info', - title: 'Toggle Schedule', - text: 'This will toggle the selected schedule.', - showCancelButton: true, - allowOutsideClick: true, - closeOnConfirm: false, - confirmButtonText: 'Continue', - showLoaderOnConfirm: true - }, function () { - $.ajax({ - method: 'POST', - url: Router.route('server.schedules.toggle', { - server: Pterodactyl.server.uuidShort, - schedule: self.data('schedule-id'), - }), - headers: { - 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), - } - }).done(function (data) { - swal({ - type: 'success', - title: '', - text: 'Schedule has been toggled.' - }); - if (data.status !== 1) { - self.parent().parent().addClass('muted muted-hover'); - } else { - self.parent().parent().removeClass('muted muted-hover'); - } - }).fail(function (jqXHR) { - console.error(jqXHR); - swal({ - type: 'error', - title: 'Whoops!', - text: 'An error occurred while attempting to toggle this schedule.' - }); - }); - }); - }); -}); diff --git a/public/themes/pterodactyl/js/frontend/tasks/view-actions.js b/public/themes/pterodactyl/js/frontend/tasks/view-actions.js deleted file mode 100644 index 86b7f8561..000000000 --- a/public/themes/pterodactyl/js/frontend/tasks/view-actions.js +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. - -$(document).ready(function () { - function setupSelect2() { - $('select[name="tasks[time_value][]"]').select2(); - $('select[name="tasks[time_interval][]"]').select2(); - $('select[name="tasks[action][]"]').select2(); - } - - setupSelect2(); - - $('[data-action="update-field"]').on('change', function (event) { - event.preventDefault(); - var updateField = $(this).data('field'); - var selected = $(this).map(function (i, opt) { - return $(opt).val(); - }).toArray(); - if (selected.length === $(this).find('option').length) { - $('input[name=' + updateField + ']').val('*'); - } else { - $('input[name=' + updateField + ']').val(selected.join(',')); - } - }); - - $('button[data-action="add-new-task"]').on('click', function () { - if ($('#containsTaskList').find('.task-list-item').length >= 5) { - swal('Task Limit Reached', 'You may only assign a maximum of 5 tasks to one schedule.'); - return; - } - - var clone = $('div[data-target="task-clone"]').clone(); - clone.insertBefore('#taskAppendBefore').removeAttr('data-target'); - clone.find('select:first').attr('selected'); - clone.find('input').val(''); - clone.find('span.select2-container').remove(); - clone.find('div[data-attribute="remove-task-element"]').addClass('input-group').find('div.input-group-btn').removeClass('hidden'); - clone.find('button[data-action="remove-task"]').on('click', function () { - clone.remove(); - }); - setupSelect2(); - $(this).data('element', clone); - }); -}); diff --git a/public/themes/pterodactyl/js/plugins/minecraft/eula.js b/public/themes/pterodactyl/js/plugins/minecraft/eula.js deleted file mode 100644 index 27e78c94c..000000000 --- a/public/themes/pterodactyl/js/plugins/minecraft/eula.js +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// 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. -$(document).ready(function () { - Socket.on('console', function (data) { - if (typeof data === 'undefined' || typeof data.line === 'undefined') { - return; - } - - if (~data.line.indexOf('You need to agree to the EULA in order to run the server')) { - swal({ - title: 'EULA Acceptance', - text: 'By pressing \'I Accept\' below you are indicating your agreement to the Mojang EULA.', - type: 'info', - html: true, - showCancelButton: true, - showConfirmButton: true, - cancelButtonText: 'I do not Accept', - confirmButtonText: 'I Accept', - closeOnConfirm: false, - showLoaderOnConfirm: true - }, function () { - $.ajax({ - type: 'POST', - url: Pterodactyl.meta.saveFile, - headers: { 'X-CSRF-Token': Pterodactyl.meta.csrfToken, }, - data: { - file: 'eula.txt', - contents: 'eula=true' - } - }).done(function (data) { - $('[data-attr="power"][data-action="start"]').trigger('click'); - swal({ - type: 'success', - title: '', - text: 'The EULA for this server has been accepted, restarting server now.', - }); - }).fail(function (jqXHR) { - console.error(jqXHR); - swal({ - title: 'Whoops!', - text: 'An error occurred while attempting to set the EULA as accepted: ' + jqXHR.responseJSON.error, - type: 'error' - }) - }); - }); - } - }); -}); diff --git a/public/themes/pterodactyl/vendor/ace/ace.js b/public/themes/pterodactyl/vendor/ace/ace.js deleted file mode 100644 index b4b7a55cb..000000000 --- a/public/themes/pterodactyl/vendor/ace/ace.js +++ /dev/null @@ -1,14 +0,0 @@ -(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE="",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0}),define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){if("ontouchmove"in e){var r,i;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];r=t.clientX,i=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,s=e.changedTouches[0];e.wheelX=-(s.clientX-r)/t,e.wheelY=-(s.clientY-i)/t,r=s.clientX,i=s.clientY,n(e)})}},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){s[e.keyCode]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n=53&&O()},I=o.delayedCall(j,50);r.addListener(n,"compositionstart",B),i.isGecko?r.addListener(n,"text",function(){I.schedule()}):(r.addListener(n,"keyup",function(){I.schedule()}),r.addListener(n,"keydown",function(){I.schedule()})),r.addListener(n,"compositionend",F),this.getElement=function(){return n},this.setReadOnly=function(e){n.readOnly=e},this.onContextMenu=function(e){L=!0,b(t.selection.isEmpty()),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){if(!o&&i.isOldIE)return;p||(p=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+"height:"+n.style.height+";"+(i.isIE?"opacity:0.1;":"");var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){n.style.left=e.clientX-l-2+"px",n.style.top=Math.min(e.clientY-f-2,c)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout(q),i.isWin&&!i.isOldIE&&r.capture(t.container,h,R)},this.onContextMenuClose=R;var q,U=function(e){t.textInput.onContextMenu(e),R()};r.addListener(n,"mouseup",U),r.addListener(n,"mousedown",function(e){e.preventDefault(),R()}),r.addListener(t.renderer.scroller,"contextmenu",U),r.addListener(n,"contextmenu",U)};t.TextInput=f}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function u(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e)),t.setDefaultHandler("touchmove",this.onTouchMove.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function a(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function f(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=0;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var r=this.editor,i=e.getButton();if(i!==0){var s=r.getSelectionRange(),o=s.isEmpty();r.$blockScrolling++,(o||i==1)&&r.selection.moveToPosition(n),r.$blockScrolling--,i==2&&r.textInput.onContextMenu(e.domEvent);return}this.mousedownEvent.time=Date.now();if(t&&!r.isFocused()){r.focus();if(this.$focusTimout&&!this.$clickSelection&&!r.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;n.$blockScrolling++,this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select"),n.$blockScrolling--},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);t.$blockScrolling++;if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.$blockScrolling--,t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);n.$blockScrolling++;if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.$blockScrolling--,n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(s.prototype),t.Tooltip=s}),define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
    "),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=f}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.rowt.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&(r.row>0||e>0)&&r.row++,this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}}}).call(r.prototype),t.TokenIterator=r}),define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g,y&&/string\.end/.test(v.type)&&(y=!1);else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";function r(e){var n=/\w{4}/g;for(var r in e)t.packages[r]=e[r].replace(n,"\\u$&")}t.packages={},r({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour/cstyle","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour/cstyle").CstyleBehaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i};(function(){this.$defaultBehaviour=new s,this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules(this.$highlightRuleConfig),this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.rowr)break;l.start.row==r&&l.start.column>=t.column&&(l.start.column!=t.column||!this.$insertRight)&&(l.start.column+=o,l.start.row+=s);if(l.end.row==r&&l.end.column>=t.column){if(l.end.column==t.column&&this.$insertRight)continue;l.end.column==t.column&&o>0&&al.start.column&&l.end.column==u[a+1].start.column&&(l.end.column-=o),l.end.column+=o,l.end.row+=s}}if(s!=0&&a=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;re.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),s=this.$wrapData,o=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;fr-b){var w=a+r-b;if(e[w-1]>=p&&e[w]>=p){y(w);continue}if(e[w]==n||e[w]==u){for(w;w!=a-1;w--)if(e[w]==n)break;if(w>a){y(w);continue}w=a+r;for(w;w>2)),a-1);while(w>E&&e[w]E&&e[w]E&&e[w]==l)w--}else while(w>E&&e[w]E){y(++w);continue}w=a+r,e[w]==t&&w--,y(w-b)}return s},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(l):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}var v=0;if(this.$useWrapMode){var m=this.$wrapData[r];if(m){var g=Math.floor(e-o);s=m[g],g>0&&m.length&&(v=m.indent,i=m[g-1]||m[m.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t-v)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=p}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i){if(!e.start){var o=e.offset+(i||0);r=new s(n,o,n,o+e.length);if(!e.length&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start))return r=null,!1}else r=e;return!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=0;u--)if(i(o[u],t,s))return!0};else var u=function(e,t,s){var o=r.getMatchOffsets(e,n);for(var u=0;u=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||0}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r||n.isDefault?r=-100:r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o0&&this.$blockScrolling--;var n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec("paste",this,n)},this.$handlePaste=function(e){typeof e=="string"&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var n=t.split(/\r\n|\r|\n/),r=this.selection.rangeList.ranges;if(n.length>r.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,t);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&np+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}).call(b.prototype),g.defineOptions(b.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.keybindingId},handlesSet:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines.length==1?null:e.lines,text:e.lines.length==1?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function n(e,t){var n=new Array(e.length);for(var r=0;r0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0},this.$serializeDeltas=function(t){return n(t,e)},this.$deserializeDeltas=function(e){return n(e,t)}}).call(r.prototype),t.UndoManager=r}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;to&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&vn.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1p,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"",e.push("
    "),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("
    "),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var l=(t.start.column?1:0)|(t.end.column?0:8);e.push("
    ")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("
    ")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("
    ")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("
    ")}}).call(s.prototype),t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n"+s.stringRepeat(this.TAB_CHAR,n)+""):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var u=s.stringRepeat(" ",this.tabSize),a=u;this.$tabStrings[" "]=""+u+"",this.$tabStrings[" "]=""+a+""}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;uf&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRowt.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>r)break;this.$useLineGroups()&&t.push("
    "),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("
    "),i++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?""+s.stringRepeat(i.SPACE_CHAR,e.length)+"":e;if(e=="&")return"&";if(e=="<")return"<";if(e==">")return">";if(e==" "){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,""+l+""}return r?""+i.SPACE_CHAR+"":(t+=1,""+e+"")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("",a,"")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]==" "?(e.push(s.stringRepeat(this.$tabStrings[" "],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,o=0,u=n[0],a=0;for(var f=0;f=u)a=this.$renderToken(e,a,l,c.substring(0,u-i)),c=c.substring(u-i),i=u,r||e.push("
    ","
    "),e.push(s.stringRepeat("\u00a0",n.indent)),o++,a=0,u=n[o]||Number.MAX_VALUE;c.length!=0&&(i+=c.length,a=this.$renderToken(e,a,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),n||e.push("
    ")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i=!("opacity"in this.element.style)),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=(i?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;ne.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;this.drawCursor?this.drawCursor(o,s,e,t[n],this.session):(o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px")}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(s.prototype),t.Cursor=s}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}';i.importCssString(m,"ace_editor.css");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new d(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.textarea.style,i=this.lineHeight;if(t<0||t>e.height-i){r.top=r.left="0";return}var s=this.characterWidth;if(this.$composition){var o=this.textarea.value.replace(/^\x01+/,"");s*=this.session.$getStringScreenWidth(o)[0]+2,i+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-s&&(n=this.$size.scrollerWidth-s),n+=this.gutterWidth,r.height=i+"px",r.width=s+"px",r.left=Math.min(n,this.$size.scrollerWidth-s)+"px",r.top=Math.min(t,this.$size.height-i)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=this.scrollTop%this.lineHeight,l=t.scrollerHeight+this.lineHeight,c=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=c;var h=this.scrollMargin;this.session.setScrollTop(Math.max(-h.top,Math.min(this.scrollTop,i-t.scrollerHeight+h.bottom))),this.session.setScrollLeft(Math.max(-h.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+h.right)));var p=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+c<0||this.scrollTop>h.top),d=a!==p;d&&(this.$vScroll=p,this.scrollBarV.setVisible(p));var v=Math.ceil(l/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-f)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),l=t.scrollerHeight+e.getRowLength(g)*w+b,f=this.scrollTop-y*w;var S=0;this.layerConfig.width!=s&&(S=this.CHANGE_H_SCROLL);if(u||d)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),d&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:l,maxHeight:i,offset:f,gutterOffset:w?Math.max(0,Math.ceil((f+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},S},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(ts?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r||!r.cssClass)throw new Error("couldn't load module "+e+" or it didn't call define");i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),u=function(t,n,r,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var s=this.$normalizePath;i=i||s(e.toUrl("ace/worker/worker.js",null,"_"));var u={};t.forEach(function(t){u[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{this.$worker=new Worker(i)}catch(a){if(!(a instanceof window.DOMException))throw a;var f=this.$workerBlob(i),l=window.URL||window.webkitURL,c=l.createObjectURL(f);this.$worker=new Worker(c),l.revokeObjectURL(c)}this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})},this.$workerBlob=function(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}}).call(u.prototype);var a=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};a.prototype=u.prototype,t.UIWorkerClient=a,t.WorkerClient=u}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges();var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
    "),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=r.getInnerText(e),e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u,t.version="1.2.6"}); - (function() { - window.require(["ace/ace"], function(a) { - if (a) { - a.config.init(true); - a.define = window.define; - } - if (!window.ace) - window.ace = a; - for (var key in a) if (a.hasOwnProperty(key)) - window.ace[key] = a[key]; - }); - })(); - \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/ext-elastic_tabstops_lite.js b/public/themes/pterodactyl/vendor/ace/ext-elastic_tabstops_lite.js deleted file mode 100644 index 756e0e109..000000000 --- a/public/themes/pterodactyl/vendor/ace/ext-elastic_tabstops_lite.js +++ /dev/null @@ -1,5 +0,0 @@ -define("ace/ext/elastic_tabstops_lite",["require","exports","module","ace/editor","ace/config"],function(e,t,n){"use strict";var r=function(e){this.$editor=e;var t=this,n=[],r=!1;this.onAfterExec=function(){r=!1,t.processRows(n),n=[]},this.onExec=function(){r=!0},this.onChange=function(e){r&&(n.indexOf(e.start.row)==-1&&n.push(e.start.row),e.end.row!=e.start.row&&n.push(e.end.row))}};(function(){this.processRows=function(e){this.$inChange=!0;var t=[];for(var n=0,r=e.length;n-1)continue;var s=this.$findCellWidthsForBlock(i),o=this.$setBlockCellWidthsToMax(s.cellWidths),u=s.firstRow;for(var a=0,f=o.length;a=0){n=this.$cellWidthsForRow(r);if(n.length==0)break;t.unshift(n),r--}var i=r+1;r=e;var s=this.$editor.session.getLength();while(r0&&(this.$editor.session.getDocument().insertInLine({row:e,column:f+1},Array(l+1).join(" ")+" "),this.$editor.session.getDocument().removeInLine(e,f,f+1),r+=l),l<0&&p>=-l&&(this.$editor.session.getDocument().removeInLine(e,f+l,f),r+=l)}},this.$izip_longest=function(e){if(!e[0])return[];var t=e[0].length,n=e.length;for(var r=1;rt&&(t=i)}var s=[];for(var o=0;o=t.length?t.length:e.length,r=[];for(var i=0;i\s+/g,">"),l=function(e,t,n){var i=r.createElement("div");i.innerHTML=f,this.element=i.firstChild,this.$init(),this.setEditor(e)};(function(){this.setEditor=function(e){e.searchBox=this,e.container.appendChild(this.element),this.editor=e},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOptions=e.querySelector(".ace_search_options"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;s.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),s.stopPropagation(e)}),s.addListener(e,"click",function(e){var n=e.target||e.srcElement,r=n.getAttribute("action");r&&t[r]?t[r]():t.$searchBarKb.commands[r]&&t.$searchBarKb.commands[r].exec(t),s.stopPropagation(e)}),s.addCommandKeyListener(e,function(e,n,r){var i=a.keyCodeToString(r),o=t.$searchBarKb.findKeyCommand(n,i);o&&o.exec&&(o.exec(t),s.stopEvent(e))}),this.$onChange=i.delayedCall(function(){t.find(!1,!1)}),s.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),s.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),s.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new u([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new u,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e.searchInput.focus()},"Ctrl-H|Command-Option-F":function(e){e.replaceBox.style.display="",e.replaceInput.focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},"Alt-Return":function(e){e.activeInput==e.replaceInput&&e.replaceAll(),e.findAll()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}}]),this.$syncOptions=function(){r.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),r.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),r.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked),this.find(!1,!1)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t,n){var i=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:n}),s=!i&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",s),this.editor._emit("findSearchBox",{match:!s}),this.highlight()},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.findAll=function(){var e=this.editor.findAll(this.searchInput.value,{regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),t=!e&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",t),this.editor._emit("findSearchBox",{match:!t}),this.highlight(),this.hide()},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.element.style.display="",this.replaceBox.style.display=t?"":"none",this.isReplace=t,e&&(this.searchInput.value=e),this.find(!1,!1,!0),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb)},this.isFocused=function(){var e=document.activeElement;return e==this.searchInput||e==this.replaceInput}}).call(l.prototype),t.SearchBox=l,t.Search=function(e,t){var n=e.searchBox||new l(e);n.show(e.session.getTextRange(),t)}}),define("ace/ext/old_ie",["require","exports","module","ace/lib/useragent","ace/tokenizer","ace/ext/searchbox","ace/mode/text"],function(require,exports,module){"use strict";function patch(obj,name,regexp,replacement){eval("obj['"+name+"']="+obj[name].toString().replace(regexp,replacement))}var MAX_TOKEN_COUNT=1e3,useragent=require("../lib/useragent"),TokenizerModule=require("../tokenizer");useragent.isIE&&useragent.isIE<10&&window.top.document.compatMode==="BackCompat"&&(useragent.isOldIE=!0);if(typeof document!="undefined"&&!document.documentElement.querySelector){useragent.isOldIE=!0;var qs=function(e,t){if(t.charAt(0)==".")var n=t.slice(1);else var r=t.match(/(\w+)=(\w+)/),i=r&&r[1],s=r&&r[2];for(var o=0;o\s+/g,">"),l=function(e,t,n){var i=r.createElement("div");i.innerHTML=f,this.element=i.firstChild,this.$init(),this.setEditor(e)};(function(){this.setEditor=function(e){e.searchBox=this,e.container.appendChild(this.element),this.editor=e},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOptions=e.querySelector(".ace_search_options"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;s.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),s.stopPropagation(e)}),s.addListener(e,"click",function(e){var n=e.target||e.srcElement,r=n.getAttribute("action");r&&t[r]?t[r]():t.$searchBarKb.commands[r]&&t.$searchBarKb.commands[r].exec(t),s.stopPropagation(e)}),s.addCommandKeyListener(e,function(e,n,r){var i=a.keyCodeToString(r),o=t.$searchBarKb.findKeyCommand(n,i);o&&o.exec&&(o.exec(t),s.stopEvent(e))}),this.$onChange=i.delayedCall(function(){t.find(!1,!1)}),s.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),s.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),s.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new u([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new u,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e.searchInput.focus()},"Ctrl-H|Command-Option-F":function(e){e.replaceBox.style.display="",e.replaceInput.focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},"Alt-Return":function(e){e.activeInput==e.replaceInput&&e.replaceAll(),e.findAll()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}}]),this.$syncOptions=function(){r.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),r.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),r.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked),this.find(!1,!1)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t,n){var i=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:n}),s=!i&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",s),this.editor._emit("findSearchBox",{match:!s}),this.highlight()},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.findAll=function(){var e=this.editor.findAll(this.searchInput.value,{regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),t=!e&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",t),this.editor._emit("findSearchBox",{match:!t}),this.highlight(),this.hide()},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.element.style.display="",this.replaceBox.style.display=t?"":"none",this.isReplace=t,e&&(this.searchInput.value=e),this.find(!1,!1,!0),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb)},this.isFocused=function(){var e=document.activeElement;return e==this.searchInput||e==this.replaceInput}}).call(l.prototype),t.SearchBox=l,t.Search=function(e,t){var n=e.searchBox||new l(e);n.show(e.session.getTextRange(),t)}}); - (function() { - window.require(["ace/ext/searchbox"], function() {}); - })(); - \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/ext-settings_menu.js b/public/themes/pterodactyl/vendor/ace/ext-settings_menu.js deleted file mode 100644 index 62a28c88b..000000000 --- a/public/themes/pterodactyl/vendor/ace/ext-settings_menu.js +++ /dev/null @@ -1,5 +0,0 @@ -define("ace/ext/menu_tools/element_generator",["require","exports","module"],function(e,t,n){"use strict";n.exports.createOption=function(t){var n,r=document.createElement("option");for(n in t)t.hasOwnProperty(n)&&(n==="selected"?r.setAttribute(n,t[n]):r[n]=t[n]);return r},n.exports.createCheckbox=function(t,n,r){var i=document.createElement("input");return i.setAttribute("type","checkbox"),i.setAttribute("id",t),i.setAttribute("name",t),i.setAttribute("value",n),i.setAttribute("class",r),n&&i.setAttribute("checked","checked"),i},n.exports.createInput=function(t,n,r){var i=document.createElement("input");return i.setAttribute("type","text"),i.setAttribute("id",t),i.setAttribute("name",t),i.setAttribute("value",n),i.setAttribute("class",r),i},n.exports.createLabel=function(t,n){var r=document.createElement("label");return r.setAttribute("for",n),r.textContent=t,r},n.exports.createSelection=function(t,r,i){var s=document.createElement("select");return s.setAttribute("id",t),s.setAttribute("name",t),s.setAttribute("class",i),r.forEach(function(e){s.appendChild(n.exports.createOption(e))}),s}}),define("ace/ext/modelist",["require","exports","module"],function(e,t,n){"use strict";function i(e){var t=a.text,n=e.split(/[\/\\]/).pop();for(var i=0;i 0!";if(e==this.$splits)return;if(e>this.$splits){while(this.$splitse)t=this.$editors[this.$splits-1],this.$container.removeChild(t.container),this.$splits--;this.resize()},this.getSplits=function(){return this.$splits},this.getEditor=function(e){return this.$editors[e]},this.getCurrentEditor=function(){return this.$cEditor},this.focus=function(){this.$cEditor.focus()},this.blur=function(){this.$cEditor.blur()},this.setTheme=function(e){this.$editors.forEach(function(t){t.setTheme(e)})},this.setKeyboardHandler=function(e){this.$editors.forEach(function(t){t.setKeyboardHandler(e)})},this.forEach=function(e,t){this.$editors.forEach(e,t)},this.$fontSize="",this.setFontSize=function(e){this.$fontSize=e,this.forEach(function(t){t.setFontSize(e)})},this.$cloneSession=function(e){var t=new a(e.getDocument(),e.getMode()),n=e.getUndoManager();if(n){var r=new l(n,t);t.setUndoManager(r)}return t.$informUndoManager=i.delayedCall(function(){t.$deltas=[]}),t.setTabSize(e.getTabSize()),t.setUseSoftTabs(e.getUseSoftTabs()),t.setOverwrite(e.getOverwrite()),t.setBreakpoints(e.getBreakpoints()),t.setUseWrapMode(e.getUseWrapMode()),t.setUseWorker(e.getUseWorker()),t.setWrapLimitRange(e.$wrapLimitRange.min,e.$wrapLimitRange.max),t.$foldData=e.$cloneFoldData(),t},this.setSession=function(e,t){var n;t==null?n=this.$cEditor:n=this.$editors[t];var r=this.$editors.some(function(t){return t.session===e});return r&&(e=this.$cloneSession(e)),n.setSession(e),e},this.getOrientation=function(){return this.$orientation},this.setOrientation=function(e){if(this.$orientation==e)return;this.$orientation=e,this.resize()},this.resize=function(){var e=this.$container.clientWidth,t=this.$container.clientHeight,n;if(this.$orientation==this.BESIDE){var r=e/this.$splits;for(var i=0;i"),o||l.push(""),f.$renderLine(l,h,!0,!1),l.push("\n
    ");var p="
    "+"
    "+l.join("")+"
    "+"
    ";return f.destroy(),{css:s+n.cssText,html:p,session:u}},n.exports=f,n.exports.highlight=f}); - (function() { - window.require(["ace/ext/static_highlight"], function() {}); - })(); - \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/ext-textarea.js b/public/themes/pterodactyl/vendor/ace/ext-textarea.js deleted file mode 100644 index 56b6eac9b..000000000 --- a/public/themes/pterodactyl/vendor/ace/ext-textarea.js +++ /dev/null @@ -1,5 +0,0 @@ -define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),define("ace/ext/textarea",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/net","ace/ace","ace/theme/textmate"],function(e,t,n){"use strict";function a(e,t){for(var n in t)e.style[n]=t[n]}function f(e,t){if(e.type!="textarea")throw new Error("Textarea required!");var n=e.parentNode,i=document.createElement("div"),s=function(){var t="position:relative;";["margin-top","margin-left","margin-right","margin-bottom"].forEach(function(n){t+=n+":"+u(e,i,n)+";"});var n=u(e,i,"width")||e.clientWidth+"px",r=u(e,i,"height")||e.clientHeight+"px";t+="height:"+r+";width:"+n+";",t+="display:inline-block;",i.setAttribute("style",t)};r.addListener(window,"resize",s),s(),n.insertBefore(i,e.nextSibling);while(n!==document){if(n.tagName.toUpperCase()==="FORM"){var o=n.onsubmit;n.onsubmit=function(n){e.value=t(),o&&o.call(this,n)};break}n=n.parentNode}return i}function l(t,n,r){s.loadScript(t,function(){e([n],r)})}function c(e,t,n,r,i,s){function a(e){return e==="true"||e==1}var o=e.getSession(),u=e.renderer;return s=s||l,e.setDisplaySettings=function(t){t==null&&(t=n.style.display=="none"),t?(n.style.display="block",n.hideButton.focus(),e.on("focus",function r(){e.removeListener("focus",r),n.style.display="none"})):e.focus()},e.$setOption=e.setOption,e.$getOption=e.getOption,e.setOption=function(t,n){switch(t){case"mode":e.$setOption("mode","ace/mode/"+n);break;case"theme":e.$setOption("theme","ace/theme/"+n);break;case"keybindings":switch(n){case"vim":e.setKeyboardHandler("ace/keyboard/vim");break;case"emacs":e.setKeyboardHandler("ace/keyboard/emacs");break;default:e.setKeyboardHandler(null)}break;case"softWrap":case"fontSize":e.$setOption(t,n);break;default:e.$setOption(t,a(n))}},e.getOption=function(t){switch(t){case"mode":return e.$getOption("mode").substr("ace/mode/".length);case"theme":return e.$getOption("theme").substr("ace/theme/".length);case"keybindings":var n=e.getKeyboardHandler();switch(n&&n.$id){case"ace/keyboard/vim":return"vim";case"ace/keyboard/emacs":return"emacs";default:return"ace"}break;default:return e.$getOption(t)}},e.setOptions(i),e}function h(e,n,i){function f(e,t,n,r){if(!n){e.push("");return}e.push("")}var s=null,o={mode:"Mode:",wrap:"Soft Wrap:",theme:"Theme:",fontSize:"Font Size:",showGutter:"Display Gutter:",keybindings:"Keyboard",showPrintMargin:"Show Print Margin:",useSoftTabs:"Use Soft Tabs:",showInvisibles:"Show Invisibles"},u={mode:{text:"Plain",javascript:"JavaScript",xml:"XML",html:"HTML",css:"CSS",scss:"SCSS",python:"Python",php:"PHP",java:"Java",ruby:"Ruby",c_cpp:"C/C++",coffee:"CoffeeScript",json:"json",perl:"Perl",clojure:"Clojure",ocaml:"OCaml",csharp:"C#",haxe:"haXe",svg:"SVG",textile:"Textile",groovy:"Groovy",liquid:"Liquid",Scala:"Scala"},theme:{clouds:"Clouds",clouds_midnight:"Clouds Midnight",cobalt:"Cobalt",crimson_editor:"Crimson Editor",dawn:"Dawn",eclipse:"Eclipse",idle_fingers:"Idle Fingers",kr_theme:"Kr Theme",merbivore:"Merbivore",merbivore_soft:"Merbivore Soft",mono_industrial:"Mono Industrial",monokai:"Monokai",pastel_on_dark:"Pastel On Dark",solarized_dark:"Solarized Dark",solarized_light:"Solarized Light",textmate:"Textmate",twilight:"Twilight",vibrant_ink:"Vibrant Ink"},showGutter:s,fontSize:{"10px":"10px","11px":"11px","12px":"12px","14px":"14px","16px":"16px"},wrap:{off:"Off",40:"40",80:"80",free:"Free"},keybindings:{ace:"ace",vim:"vim",emacs:"emacs"},showPrintMargin:s,useSoftTabs:s,showInvisibles:s},a=[];a.push("");for(var l in t.defaultOptions)a.push(""),a.push("");a.push("
    SettingValue
    ",o[l],""),f(a,l,u[l],i.getOption(l)),a.push("
    "),e.innerHTML=a.join("");var c=function(e){var t=e.currentTarget;i.setOption(t.title,t.value)},h=function(e){var t=e.currentTarget;i.setOption(t.title,t.checked)},p=e.getElementsByTagName("select");for(var d=0;d0&&!(s%l)&&!(f%l)&&(r[l]=(r[l]||0)+1),n[f]=(n[f]||0)+1}s=f}while(up.score&&(p={score:v,length:u})}if(p.score&&p.score>1.4)var m=p.length;if(i>d+1){if(m==1||di+1)return{ch:" ",length:m}},t.detectIndentation=function(e){var n=e.getLines(0,1e3),r=t.$detectIndentation(n)||{};return r.ch&&e.setUseSoftTabs(r.ch==" "),r.length&&e.setTabSize(r.length),r},t.trimTrailingSpace=function(e,t){var n=e.getDocument(),r=n.getAllLines(),i=t?-1:0;for(var s=0,o=r.length;si&&n.removeInLine(s,a,u.length)}},t.convertIndentation=function(e,t,n){var i=e.getTabString()[0],s=e.getTabSize();n||(n=s),t||(t=i);var o=t==" "?t:r.stringRepeat(t,n),u=e.doc,a=u.getAllLines(),f={},l={};for(var c=0,h=a.length;c30&&this.$data.shift()},append:function(e){var t=this.$data.length-1,n=this.$data[t]||"";e&&(n+=e),n&&(this.$data[t]=n)},get:function(e){return e=e||1,this.$data.slice(this.$data.length-e,this.$data.length).reverse().join("\n")},pop:function(){return this.$data.length>1&&this.$data.pop(),this.get()},rotate:function(){return this.$data.unshift(this.$data.pop()),this.get()}}}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/keybinding-vim.js b/public/themes/pterodactyl/vendor/ace/keybinding-vim.js deleted file mode 100644 index 9900e9f8e..000000000 --- a/public/themes/pterodactyl/vendor/ace/keybinding-vim.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/keyboard/vim",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/dom","ace/lib/oop","ace/lib/keys","ace/lib/event","ace/search","ace/lib/useragent","ace/search_highlight","ace/commands/multi_select_commands","ace/mode/text","ace/multi_select"],function(e,t,n){"use strict";function r(){function t(e){return typeof e!="object"?e+"":"line"in e?e.line+":"+e.ch:"anchor"in e?t(e.anchor)+"->"+t(e.head):Array.isArray(e)?"["+e.map(function(e){return t(e)})+"]":JSON.stringify(e)}var e="";for(var n=0;n"):!1}function M(e){var t=e.state.vim;return t.onPasteFn||(t.onPasteFn=function(){t.insertMode||(e.setCursor(St(e.getCursor(),0,1)),yt.enterInsertMode(e,{},t))}),t.onPasteFn}function H(e,t){var n=[];for(var r=e;r=e.firstLine()&&t<=e.lastLine()}function U(e){return/^[a-z]$/.test(e)}function z(e){return"()[]{}".indexOf(e)!=-1}function W(e){return _.test(e)}function X(e){return/^[A-Z]$/.test(e)}function V(e){return/^\s*$/.test(e)}function $(e,t){for(var n=0;n"){var n=t.length-11,r=e.slice(0,n),i=t.slice(0,n);return r==i&&e.length>n?"full":i.indexOf(r)==0?"partial":!1}return e==t?"full":t.indexOf(e)==0?"partial":!1}function Ct(e){var t=/^.*(<[\w\-]+>)$/.exec(e),n=t?t[1]:e.slice(-1);if(n.length>1)switch(n){case"":n="\n";break;case"":n=" ";break;default:}return n}function kt(e,t,n){return function(){for(var r=0;r2&&(t=Mt.apply(undefined,Array.prototype.slice.call(arguments,1))),Ot(e,t)?e:t}function _t(e,t){return arguments.length>2&&(t=_t.apply(undefined,Array.prototype.slice.call(arguments,1))),Ot(e,t)?t:e}function Dt(e,t,n){var r=Ot(e,t),i=Ot(t,n);return r&&i}function Pt(e,t){return e.getLine(t).length}function Ht(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function Bt(e){return e.replace(/([.?*+$\[\]\/\\(){}|\-])/g,"\\$1")}function jt(e,t,n){var r=Pt(e,t),i=(new Array(n-r+1)).join(" ");e.setCursor(E(t,r)),e.replaceRange(i,e.getCursor())}function Ft(e,t){var n=[],r=e.listSelections(),i=Lt(e.clipPos(t)),s=!At(t,i),o=e.getCursor("head"),u=qt(r,o),a=At(r[u].head,r[u].anchor),f=r.length-1,l=f-u>u?f:0,c=r[l].anchor,h=Math.min(c.line,i.line),p=Math.max(c.line,i.line),d=c.ch,v=i.ch,m=r[l].head.ch-d,g=v-d;m>0&&g<=0?(d++,s||v--):m<0&&g>=0?(d--,a||v++):m<0&&g==-1&&(d--,v++);for(var y=h;y<=p;y++){var b={anchor:new E(y,d),head:new E(y,v)};n.push(b)}return u=i.line==p?n.length-1:0,e.setSelections(n),t.ch=v,c.ch=d,c}function It(e,t,n){var r=[];for(var i=0;ia&&(i.line=a),i.ch=Pt(e,i.line)}else i.ch=0,s.ch=Pt(e,s.line);return{ranges:[{anchor:s,head:i}],primary:0}}if(n=="block"){var f=Math.min(s.line,i.line),l=Math.min(s.ch,i.ch),c=Math.max(s.line,i.line),h=Math.max(s.ch,i.ch)+1,p=c-f+1,d=i.line==f?0:p-1,v=[];for(var m=0;m0&&s&&V(s);s=i.pop())n.line--,n.ch=0;s?(n.line--,n.ch=Pt(e,n.line)):n.ch=0}}function Kt(e,t,n){t.ch=0,n.ch=0,n.line++}function Qt(e){if(!e)return 0;var t=e.search(/\S/);return t==-1?e.length:t}function Gt(e,t,n,r,i){var s=Vt(e),o=e.getLine(s.line),u=s.ch,a=i?D[0]:P[0];while(!a(o.charAt(u))){u++;if(u>=o.length)return null}r?a=P[0]:(a=D[0],a(o.charAt(u))||(a=D[1]));var f=u,l=u;while(a(o.charAt(f))&&f=0)l--;l++;if(t){var c=f;while(/\s/.test(o.charAt(f))&&f0)l--;l||(l=h)}}return{start:E(s.line,l),end:E(s.line,f)}}function Yt(e,t,n){At(t,n)||nt.jumpList.add(e,t,n)}function Zt(e,t){nt.lastChararacterSearch.increment=e,nt.lastChararacterSearch.forward=t.forward,nt.lastChararacterSearch.selectedCharacter=t.selectedCharacter}function nn(e,t,n,r){var i=Lt(e.getCursor()),s=n?1:-1,o=n?e.lineCount():-1,u=i.ch,a=i.line,f=e.getLine(a),l={lineText:f,nextCh:f.charAt(u),lastCh:null,index:u,symb:r,reverseSymb:(n?{")":"(","}":"{"}:{"(":")","{":"}"})[r],forward:n,depth:0,curMoveThrough:!1},c=en[r];if(!c)return i;var h=tn[c].init,p=tn[c].isComplete;h&&h(l);while(a!==o&&t){l.index+=s,l.nextCh=l.lineText.charAt(l.index);if(!l.nextCh){a+=s,l.lineText=e.getLine(a)||"";if(s>0)l.index=0;else{var d=l.lineText.length;l.index=d>0?d-1:0}l.nextCh=l.lineText.charAt(l.index)}p(l)&&(i.line=a,i.ch=l.index,t--)}return l.nextCh||l.curMoveThrough?E(a,l.index):i}function rn(e,t,n,r,i){var s=t.line,o=t.ch,u=e.getLine(s),a=n?1:-1,f=r?P:D;if(i&&u==""){s+=a,u=e.getLine(s);if(!R(e,s))return null;o=n?0:u.length}for(;;){if(i&&u=="")return{from:0,to:0,line:s};var l=a>0?u.length:-1,c=l,h=l;while(o!=l){var p=!1;for(var d=0;d0?0:u.length}throw new Error("The impossible happened.")}function sn(e,t,n,r,i,s){var o=Lt(t),u=[];(r&&!i||!r&&i)&&n++;var a=!r||!i;for(var f=0;f0?1:-1;var n=e.ace.session.getFoldLine(t);n&&t+r>n.start.row&&t+r0?n.end.row:n.start.row)-t)}var s=t.line,o=e.firstLine(),u=e.lastLine(),a,f,l=s;if(r){while(o<=l&&l<=u&&n>0)p(l),h(l,r)&&n--,l+=r;return new E(l,0)}var d=e.state.vim;if(d.visualLine&&h(s,1,!0)){var v=d.sel.anchor;h(v.line,-1,!0)&&(!i||v.line!=s)&&(s+=1)}var m=c(s);for(l=s;l<=u&&n;l++)h(l,1,!0)&&(!i||c(l)!=m)&&n--;f=new E(l,0),l>u&&!m?m=!0:i=!1;for(l=s;l>o;l--)if(!i||c(l)==m||l==s)if(h(l,-1,!0))break;return a=new E(l,0),{start:a,end:f}}function cn(e,t,n,r){var i=t,s,o,u={"(":/[()]/,")":/[()]/,"[":/[[\]]/,"]":/[[\]]/,"{":/[{}]/,"}":/[{}]/}[n],a={"(":"(",")":"(","[":"[","]":"[","{":"{","}":"{"}[n],f=e.getLine(i.line).charAt(i.ch),l=f===a?1:0;s=e.scanForBracket(E(i.line,i.ch+l),-1,null,{bracketRegex:u}),o=e.scanForBracket(E(i.line,i.ch+l),1,null,{bracketRegex:u});if(!s||!o)return{start:i,end:i};s=s.pos,o=o.pos;if(s.line==o.line&&s.ch>o.ch||s.line>o.line){var c=s;s=o,o=c}return r?o.ch+=1:s.ch+=1,{start:s,end:o}}function hn(e,t,n,r){var i=Lt(t),s=e.getLine(i.line),o=s.split(""),u,a,f,l,c=o.indexOf(n);i.ch-1&&!u;f--)o[f]==n&&(u=f+1);if(u&&!a)for(f=u,l=o.length;f'+t+"",{bottom:!0,duration:5e3}):alert(t)}function Nn(e,t){var n="";return e&&(n+=''+e+""),n+=' ',t&&(n+='',n+=t,n+=""),n}function kn(e,t){var n=(t.prefix||"")+" "+(t.desc||""),r=Nn(t.prefix,t.desc);vn(e,r,n,t.onClose,t)}function Ln(e,t){if(e instanceof RegExp&&t instanceof RegExp){var n=["global","multiline","ignoreCase","source"];for(var r=0;r=t&&e<=n:e==t}function Hn(e){var t=e.ace.renderer;return{top:t.getFirstFullyVisibleRow(),bottom:t.getLastFullyVisibleRow()}}function In(e,t,n,r,i,s,o,u,a){function c(){e.operation(function(){while(!f)h(),p();d()})}function h(){var t=e.getRange(s.from(),s.to()),n=t.replace(o,u);s.replace(n)}function p(){while(s.findNext()&&Pn(s.from(),r,i)){if(!n&&l&&s.from().line==l.line)continue;e.scrollIntoView(s.from(),30),e.setSelection(s.from(),s.to()),l=s.from(),f=!1;return}f=!0}function d(t){t&&t(),e.focus();if(l){e.setCursor(l);var n=e.state.vim;n.exMode=!1,n.lastHPos=n.lastHSPos=l.ch}a&&a()}function m(t,n,r){v.e_stop(t);var i=v.keyName(t);switch(i){case"Y":h(),p();break;case"N":p();break;case"A":var s=a;a=undefined,e.operation(c),a=s;break;case"L":h();case"Q":case"Esc":case"Ctrl-C":case"Ctrl-[":d(r)}return f&&d(r),!0}e.state.vim.exMode=!0;var f=!1,l=s.from();p();if(f){Tn(e,"No matches for "+o.source);return}if(!t){c(),a&&a();return}kn(e,{prefix:"replace with "+u+" (y/n/a/q/l)",onKeyDown:m})}function qn(e){var t=e.state.vim,n=nt.macroModeState,r=nt.registerController.getRegister("."),i=n.isPlaying,s=n.lastInsertModeChanges,o=[];if(!i){var u=s.inVisualBlock?t.lastSelection.visualBlock.height:1,a=s.changes,o=[],f=0;while(f1&&(Zn(e,t,t.insertModeRepeat-1,!0),t.lastEditInputState.repeatOverride=t.insertModeRepeat),delete t.insertModeRepeat,t.insertMode=!1,e.setCursor(e.getCursor().line,e.getCursor().ch-1),e.setOption("keyMap","vim"),e.setOption("disableInput",!0),e.toggleOverwrite(!1),r.setText(s.changes.join("")),v.signal(e,"vim-mode-change",{mode:"normal"}),n.isRecording&&Xn(n)}function Rn(e){b.unshift(e)}function Un(e,t,n,r,i){var s={keys:e,type:t};s[t]=n,s[t+"Args"]=r;for(var o in i)s[o]=i[o];Rn(s)}function zn(e,t,n,r){var i=nt.registerController.getRegister(r);if(r==":"){i.keyBuffer[0]&&Fn.processCommand(e,i.keyBuffer[0]),n.isPlaying=!1;return}var s=i.keyBuffer,o=0;n.isPlaying=!0,n.replaySearchQueries=i.searchQueries.slice(0);for(var u=0;u|<\w+>|./.exec(a),l=f[0],a=a.substring(f.index+l.length),v.Vim.handleKey(e,l,"macro");if(t.insertMode){var c=i.insertModeChanges[o++].changes;nt.macroModeState.lastInsertModeChanges.changes=c,er(e,c,1),qn(e)}}}n.isPlaying=!1}function Wn(e,t){if(e.isPlaying)return;var n=e.latestRegister,r=nt.registerController.getRegister(n);r&&r.pushText(t)}function Xn(e){if(e.isPlaying)return;var t=e.latestRegister,n=nt.registerController.getRegister(t);n&&n.pushInsertModeChanges&&n.pushInsertModeChanges(e.lastInsertModeChanges)}function Vn(e,t){if(e.isPlaying)return;var n=e.latestRegister,r=nt.registerController.getRegister(n);r&&r.pushSearchQuery&&r.pushSearchQuery(t)}function $n(e,t){var n=nt.macroModeState,r=n.lastInsertModeChanges;if(!n.isPlaying)while(t){r.expectCursorActivityForChange=!0;if(t.origin=="+input"||t.origin=="paste"||t.origin===undefined){var i=t.text.join("\n");r.maybeReset&&(r.changes=[],r.maybeReset=!1),r.changes.push(i)}t=t.next}}function Jn(e){var t=e.state.vim;if(t.insertMode){var n=nt.macroModeState;if(n.isPlaying)return;var r=n.lastInsertModeChanges;r.expectCursorActivityForChange?r.expectCursorActivityForChange=!1:r.maybeReset=!0}else e.curOp.isVimOp||Qn(e,t);t.visualMode&&Kn(e)}function Kn(e){var t=e.state.vim,n=wt(e,Lt(t.sel.head)),r=St(n,0,1);t.fakeCursor&&t.fakeCursor.clear(),t.fakeCursor=e.markText(n,r,{className:"cm-animate-fat-cursor"})}function Qn(e,t){var n=e.getCursor("anchor"),r=e.getCursor("head");t.visualMode&&!e.somethingSelected()?$t(e,!1):!t.visualMode&&!t.insertMode&&e.somethingSelected()&&(t.visualMode=!0,t.visualLine=!1,v.signal(e,"vim-mode-change",{mode:"visual"}));if(t.visualMode){var i=Ot(r,n)?0:-1,s=Ot(r,n)?-1:0;r=St(r,0,i),n=St(n,0,s),t.sel={anchor:n,head:r},an(e,t,"<",Mt(r,n)),an(e,t,">",_t(r,n))}else t.insertMode||(t.lastHPos=e.getCursor().ch)}function Gn(e){this.keyName=e}function Yn(e){function i(){return n.maybeReset&&(n.changes=[],n.maybeReset=!1),n.changes.push(new Gn(r)),!0}var t=nt.macroModeState,n=t.lastInsertModeChanges,r=v.keyName(e);if(!r)return;(r.indexOf("Delete")!=-1||r.indexOf("Backspace")!=-1)&&v.lookupKey(r,"vim-insert",i)}function Zn(e,t,n,r){function u(){s?ht.processAction(e,t,t.lastEditActionCommand):ht.evalInput(e,t)}function a(n){if(i.lastInsertModeChanges.changes.length>0){n=t.lastEditActionCommand?n:1;var r=i.lastInsertModeChanges;er(e,r.changes,n)}}var i=nt.macroModeState;i.isPlaying=!0;var s=!!t.lastEditActionCommand,o=t.inputState;t.inputState=t.lastEditInputState;if(s&&t.lastEditActionCommand.interlaceInsertRepeat)for(var f=0;f1&&t[0]=="n"&&(t=t.replace("numpad","")),t=tr[t]||t;var r="";return n.ctrlKey&&(r+="C-"),n.altKey&&(r+="A-"),n.shiftKey&&(r+="S-"),r+=t,r.length>1&&(r="<"+r+">"),r}function ir(e){var t=new e.constructor;return Object.keys(e).forEach(function(n){var r=e[n];Array.isArray(r)?r=r.slice():r&&typeof r=="object"&&r.constructor!=Object&&(r=ir(r)),t[n]=r}),e.sel&&(t.sel={head:e.sel.head&&Lt(e.sel.head),anchor:e.sel.anchor&&Lt(e.sel.anchor)}),t}function sr(e,t,n){var r=!1,i=S.maybeInitVimState_(e),s=i.visualBlock||i.wasInVisualBlock;i.wasInVisualBlock&&!e.ace.inMultiSelectMode?i.wasInVisualBlock=!1:e.ace.inMultiSelectMode&&i.visualBlock&&(i.wasInVisualBlock=!0);if(t==""&&!i.insertMode&&!i.visualMode&&e.ace.inMultiSelectMode)e.ace.exitMultiSelectMode();else if(s||!e.ace.inMultiSelectMode||e.ace.inVirtualSelectionMode)r=S.handleKey(e,t,n);else{var o=ir(i);e.operation(function(){e.ace.forEachSelection(function(){var i=e.ace.selection;e.state.vim.lastHPos=i.$desiredColumn==null?i.lead.column:i.$desiredColumn;var s=e.getCursor("head"),u=e.getCursor("anchor"),a=Ot(s,u)?0:-1,f=Ot(s,u)?-1:0;s=St(s,0,a),u=St(u,0,f),e.state.vim.sel.head=s,e.state.vim.sel.anchor=u,r=rr(e,t,n),i.$desiredColumn=e.state.vim.lastHPos==-1?null:e.state.vim.lastHPos,e.virtualSelectionMode()&&(e.state.vim=ir(o))}),e.curOp.cursorActivity&&!r&&(e.curOp.cursorActivity=!1)},!0)}return r}function ar(e,t){t.off("beforeEndOperation",ar);var n=t.state.cm.vimCmd;n&&t.execCommand(n.exec?n:n.name,n.args),t.curOp=t.prevOp}var i=e("../range").Range,s=e("../lib/event_emitter").EventEmitter,o=e("../lib/dom"),u=e("../lib/oop"),a=e("../lib/keys"),f=e("../lib/event"),l=e("../search").Search,c=e("../lib/useragent"),h=e("../search_highlight").SearchHighlight,p=e("../commands/multi_select_commands"),d=e("../mode/text").Mode.prototype.tokenRe;e("../multi_select");var v=function(e){this.ace=e,this.state={},this.marks={},this.$uid=0,this.onChange=this.onChange.bind(this),this.onSelectionChange=this.onSelectionChange.bind(this),this.onBeforeEndOperation=this.onBeforeEndOperation.bind(this),this.ace.on("change",this.onChange),this.ace.on("changeSelection",this.onSelectionChange),this.ace.on("beforeEndOperation",this.onBeforeEndOperation)};v.Pos=function(e,t){if(!(this instanceof E))return new E(e,t);this.line=e,this.ch=t},v.defineOption=function(e,t,n){},v.commands={redo:function(e){e.ace.redo()},undo:function(e){e.ace.undo()},newlineAndIndent:function(e){e.ace.insert("\n")}},v.keyMap={},v.addClass=v.rmClass=v.e_stop=function(){},v.keyName=function(e){if(e.key)return e.key;var t=a[e.keyCode]||"";return t.length==1&&(t=t.toUpperCase()),t=f.getModifierString(e).replace(/(^|-)\w/g,function(e){return e.toUpperCase()})+t,t},v.keyMap["default"]=function(e){return function(t){var n=t.ace.commands.commandKeyBinding[e.toLowerCase()];return n&&t.ace.execCommand(n)!==!1}},v.lookupKey=function fr(e,t,n){typeof t=="string"&&(t=v.keyMap[t]);var r=typeof t=="function"?t(e):t[e];if(r===!1)return"nothing";if(r==="...")return"multi";if(r!=null&&n(r))return"handled";if(t.fallthrough){if(!Array.isArray(t.fallthrough))return fr(e,t.fallthrough,n);for(var i=0;i0){a.row+=s,a.column+=a.row==r.row?o:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}};var e=function(e,t,n,r){this.cm=e,this.id=t,this.row=n,this.column=r,e.marks[this.id]=this};e.prototype.clear=function(){delete this.cm.marks[this.id]},e.prototype.find=function(){return g(this)},this.setBookmark=function(t,n){var r=new e(this,this.$uid++,t.line,t.ch);if(!n||!n.insertLeft)r.$insertRight=!0;return this.marks[r.id]=r,r},this.moveH=function(e,t){if(t=="char"){var n=this.ace.selection;n.clearSelection(),n.moveCursorBy(0,e)}},this.findPosV=function(e,t,n,r){if(n=="page"){var i=this.ace.renderer,s=i.layerConfig;t*=Math.floor(s.height/s.lineHeight),n="line"}if(n=="line"){var o=this.ace.session.documentToScreenPosition(e.line,e.ch);r!=null&&(o.column=r),o.row+=t,o.row=Math.min(Math.max(0,o.row),this.ace.session.getScreenLength()-1);var u=this.ace.session.screenToDocumentPosition(o.row,o.column);return g(u)}debugger},this.charCoords=function(e,t){if(t=="div"||!t){var n=this.ace.session.documentToScreenPosition(e.line,e.ch);return{left:n.column,top:n.row}}if(t=="local"){var r=this.ace.renderer,n=this.ace.session.documentToScreenPosition(e.line,e.ch),i=r.layerConfig.lineHeight,s=r.layerConfig.characterWidth,o=i*n.row;return{left:n.column*s,top:o,bottom:o+i}}},this.coordsChar=function(e,t){var n=this.ace.renderer;if(t=="local"){var r=Math.max(0,Math.floor(e.top/n.lineHeight)),i=Math.max(0,Math.floor(e.left/n.characterWidth)),s=n.session.screenToDocumentPosition(r,i);return g(s)}if(t=="div")throw"not implemented"},this.getSearchCursor=function(e,t,n){var r=!1,i=!1;e instanceof RegExp&&!e.global&&(r=!e.ignoreCase,e=e.source,i=!0);var s=new l;t.ch==undefined&&(t.ch=Number.MAX_VALUE);var o={row:t.line,column:t.ch},u=this,a=null;return{findNext:function(){return this.find(!1)},findPrevious:function(){return this.find(!0)},find:function(t){s.setOptions({needle:e,caseSensitive:r,wrap:!1,backwards:t,regExp:i,start:a||o});var n=s.find(u.ace.session);return n&&n.isEmpty()&&u.getLine(n.start.row).length==n.start.column&&(s.$options.start=n,n=s.find(u.ace.session)),a=n,a},from:function(){return a&&g(a.start)},to:function(){return a&&g(a.end)},replace:function(e){a&&(a.end=u.ace.session.doc.replace(a,e))}}},this.scrollTo=function(e,t){var n=this.ace.renderer,r=n.layerConfig,i=r.maxHeight;i-=(n.$size.scrollerHeight-n.lineHeight)*n.$scrollPastEnd,t!=null&&this.ace.session.setScrollTop(Math.max(0,Math.min(t,i))),e!=null&&this.ace.session.setScrollLeft(Math.max(0,Math.min(e,r.width)))},this.scrollInfo=function(){return 0},this.scrollIntoView=function(e,t){if(e){var n=this.ace.renderer,r={top:0,bottom:t};n.scrollCursorIntoView(m(e),n.lineHeight*2/n.$size.scrollerHeight,r)}},this.getLine=function(e){return this.ace.session.getLine(e)},this.getRange=function(e,t){return this.ace.session.getTextRange(new i(e.line,e.ch,t.line,t.ch))},this.replaceRange=function(e,t,n){return n||(n=t),this.ace.session.replace(new i(t.line,t.ch,n.line,n.ch),e)},this.replaceSelections=function(e){var t=this.ace.selection;if(this.ace.inVirtualSelectionMode){this.ace.session.replace(t.getRange(),e[0]||"");return}t.inVirtualSelectionMode=!0;var n=t.rangeList.ranges;n.length||(n=[this.ace.multiSelect.getRange()]);for(var r=n.length;r--;)this.ace.session.replace(n[r],e[r]||"");t.inVirtualSelectionMode=!1},this.getSelection=function(){return this.ace.getSelectedText()},this.getSelections=function(){return this.listSelections().map(function(e){return this.getRange(e.anchor,e.head)},this)},this.getInputField=function(){return this.ace.textInput.getElement()},this.getWrapperElement=function(){return this.ace.containter};var t={indentWithTabs:"useSoftTabs",indentUnit:"tabSize",tabSize:"tabSize",firstLineNumber:"firstLineNumber",readOnly:"readOnly"};this.setOption=function(e,n){this.state[e]=n;switch(e){case"indentWithTabs":e=t[e],n=!n;break;default:e=t[e]}e&&this.ace.setOption(e,n)},this.getOption=function(e,n){var r=t[e];r&&(n=this.ace.getOption(r));switch(e){case"indentWithTabs":return e=t[e],!n}return r?n:this.state[e]},this.toggleOverwrite=function(e){return this.state.overwrite=e,this.ace.setOverwrite(e)},this.addOverlay=function(e){if(!this.$searchHighlight||!this.$searchHighlight.session){var t=new h(null,"ace_highlight-marker","text"),n=this.ace.session.addDynamicMarker(t);t.id=n.id,t.session=this.ace.session,t.destroy=function(e){t.session.off("change",t.updateOnChange),t.session.off("changeEditor",t.destroy),t.session.removeMarker(t.id),t.session=null},t.updateOnChange=function(e){var n=e.start.row;n==e.end.row?t.cache[n]=undefined:t.cache.splice(n,t.cache.length)},t.session.on("changeEditor",t.destroy),t.session.on("change",t.updateOnChange)}var r=new RegExp(e.query.source,"gmi");this.$searchHighlight=e.highlight=t,this.$searchHighlight.setRegexp(r),this.ace.renderer.updateBackMarkers()},this.removeOverlay=function(e){this.$searchHighlight&&this.$searchHighlight.session&&this.$searchHighlight.destroy()},this.getScrollInfo=function(){var e=this.ace.renderer,t=e.layerConfig;return{left:e.scrollLeft,top:e.scrollTop,height:t.maxHeight,width:t.width,clientHeight:t.height,clientWidth:t.width}},this.getValue=function(){return this.ace.getValue()},this.setValue=function(e){return this.ace.setValue(e)},this.getTokenTypeAt=function(e){var t=this.ace.session.getTokenAt(e.line,e.ch);return t&&/comment|string/.test(t.type)?"string":""},this.findMatchingBracket=function(e){var t=this.ace.session.findMatchingBracket(m(e));return{to:t&&g(t)}},this.indentLine=function(e,t){t===!0?this.ace.session.indentRows(e,e," "):t===!1&&this.ace.session.outdentRows(new i(e,0,e,0))},this.indexFromPos=function(e){return this.ace.session.doc.positionToIndex(m(e))},this.posFromIndex=function(e){return g(this.ace.session.doc.indexToPosition(e))},this.focus=function(e){return this.ace.focus()},this.blur=function(e){return this.ace.blur()},this.defaultTextHeight=function(e){return this.ace.renderer.layerConfig.lineHeight},this.scanForBracket=function(e,t,n,r){var i=r.bracketRegex.source;if(t==1)var s=this.ace.session.$findClosingBracket(i.slice(1,2),m(e),/paren|text/);else var s=this.ace.session.$findOpeningBracket(i.slice(-2,-1),{row:e.line,column:e.ch+1},/paren|text/);return s&&{pos:g(s)}},this.refresh=function(){return this.ace.resize(!0)},this.getMode=function(){return{name:this.getOption("mode")}}}.call(v.prototype);var y=v.StringStream=function(e,t){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0};y.prototype={eol:function(){return this.pos>=this.string.length},sol:function(){return this.pos==this.lineStart},peek:function(){return this.string.charAt(this.pos)||undefined},next:function(){if(this.post},eatSpace:function(){var e=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos)))++this.pos;return this.pos>e},skipToEnd:function(){this.pos=this.string.length},skipTo:function(e){var t=this.string.indexOf(e,this.pos);if(t>-1)return this.pos=t,!0},backUp:function(e){this.pos-=e},column:function(){throw"not implemented"},indentation:function(){throw"not implemented"},match:function(e,t,n){if(typeof e!="string"){var s=this.string.slice(this.pos).match(e);return s&&s.index>0?null:(s&&t!==!1&&(this.pos+=s[0].length),s)}var r=function(e){return n?e.toLowerCase():e},i=this.string.substr(this.pos,e.length);if(r(i)==r(e))return t!==!1&&(this.pos+=e.length),!0},current:function(){return this.string.slice(this.start,this.pos)},hideFirstChars:function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}}},v.defineExtension=function(e,t){v.prototype[e]=t},o.importCssString(".normal-mode .ace_cursor{ border: 1px solid red; background-color: red; opacity: 0.5;}.normal-mode .ace_hidden-cursors .ace_cursor{ background-color: transparent;}.ace_dialog { position: absolute; left: 0; right: 0; background: white; z-index: 15; padding: .1em .8em; overflow: hidden; color: #333;}.ace_dialog-top { border-bottom: 1px solid #eee; top: 0;}.ace_dialog-bottom { border-top: 1px solid #eee; bottom: 0;}.ace_dialog input { border: none; outline: none; background: transparent; width: 20em; color: inherit; font-family: monospace;}","vimMode"),function(){function e(e,t,n){var r=e.ace.container,i;return i=r.appendChild(document.createElement("div")),n?i.className="ace_dialog ace_dialog-bottom":i.className="ace_dialog ace_dialog-top",typeof t=="string"?i.innerHTML=t:i.appendChild(t),i}function t(e,t){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=t}v.defineExtension("openDialog",function(n,r,i){function a(e){if(typeof e=="string")f.value=e;else{if(o)return;o=!0,s.parentNode.removeChild(s),u.focus(),i.onClose&&i.onClose(s)}}if(this.virtualSelectionMode())return;i||(i={}),t(this,null);var s=e(this,n,i.bottom),o=!1,u=this,f=s.getElementsByTagName("input")[0],l;if(f)i.value&&(f.value=i.value,i.select!==!1&&f.select()),i.onInput&&v.on(f,"input",function(e){i.onInput(e,f.value,a)}),i.onKeyUp&&v.on(f,"keyup",function(e){i.onKeyUp(e,f.value,a)}),v.on(f,"keydown",function(e){if(i&&i.onKeyDown&&i.onKeyDown(e,f.value,a))return;if(e.keyCode==27||i.closeOnEnter!==!1&&e.keyCode==13)f.blur(),v.e_stop(e),a();e.keyCode==13&&r(f.value)}),i.closeOnBlur!==!1&&v.on(f,"blur",a),f.focus();else if(l=s.getElementsByTagName("button")[0])v.on(l,"click",function(){a(),u.focus()}),i.closeOnBlur!==!1&&v.on(l,"blur",a),l.focus();return a}),v.defineExtension("openNotification",function(n,r){function a(){if(s)return;s=!0,clearTimeout(o),i.parentNode.removeChild(i)}if(this.virtualSelectionMode())return;t(this,a);var i=e(this,n,r&&r.bottom),s=!1,o,u=r&&typeof r.duration!="undefined"?r.duration:5e3;return v.on(i,"click",function(e){v.e_preventDefault(e),a()}),u&&(o=setTimeout(a,u)),a})}();var b=[{keys:"",type:"keyToKey",toKeys:"h"},{keys:"",type:"keyToKey",toKeys:"l"},{keys:"",type:"keyToKey",toKeys:"k"},{keys:"",type:"keyToKey",toKeys:"j"},{keys:"",type:"keyToKey",toKeys:"l"},{keys:"",type:"keyToKey",toKeys:"h",context:"normal"},{keys:"",type:"keyToKey",toKeys:"W"},{keys:"",type:"keyToKey",toKeys:"B",context:"normal"},{keys:"",type:"keyToKey",toKeys:"w"},{keys:"",type:"keyToKey",toKeys:"b",context:"normal"},{keys:"",type:"keyToKey",toKeys:"j"},{keys:"",type:"keyToKey",toKeys:"k"},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:"",context:"insert"},{keys:"",type:"keyToKey",toKeys:"",context:"insert"},{keys:"s",type:"keyToKey",toKeys:"cl",context:"normal"},{keys:"s",type:"keyToKey",toKeys:"c",context:"visual"},{keys:"S",type:"keyToKey",toKeys:"cc",context:"normal"},{keys:"S",type:"keyToKey",toKeys:"VdO",context:"visual"},{keys:"",type:"keyToKey",toKeys:"0"},{keys:"",type:"keyToKey",toKeys:"$"},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:""},{keys:"",type:"keyToKey",toKeys:"j^",context:"normal"},{keys:"H",type:"motion",motion:"moveToTopLine",motionArgs:{linewise:!0,toJumplist:!0}},{keys:"M",type:"motion",motion:"moveToMiddleLine",motionArgs:{linewise:!0,toJumplist:!0}},{keys:"L",type:"motion",motion:"moveToBottomLine",motionArgs:{linewise:!0,toJumplist:!0}},{keys:"h",type:"motion",motion:"moveByCharacters",motionArgs:{forward:!1}},{keys:"l",type:"motion",motion:"moveByCharacters",motionArgs:{forward:!0}},{keys:"j",type:"motion",motion:"moveByLines",motionArgs:{forward:!0,linewise:!0}},{keys:"k",type:"motion",motion:"moveByLines",motionArgs:{forward:!1,linewise:!0}},{keys:"gj",type:"motion",motion:"moveByDisplayLines",motionArgs:{forward:!0}},{keys:"gk",type:"motion",motion:"moveByDisplayLines",motionArgs:{forward:!1}},{keys:"w",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!1}},{keys:"W",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!1,bigWord:!0}},{keys:"e",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!0,inclusive:!0}},{keys:"E",type:"motion",motion:"moveByWords",motionArgs:{forward:!0,wordEnd:!0,bigWord:!0,inclusive:!0}},{keys:"b",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!1}},{keys:"B",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!1,bigWord:!0}},{keys:"ge",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!0,inclusive:!0}},{keys:"gE",type:"motion",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!0,bigWord:!0,inclusive:!0}},{keys:"{",type:"motion",motion:"moveByParagraph",motionArgs:{forward:!1,toJumplist:!0}},{keys:"}",type:"motion",motion:"moveByParagraph",motionArgs:{forward:!0,toJumplist:!0}},{keys:"",type:"motion",motion:"moveByPage",motionArgs:{forward:!0}},{keys:"",type:"motion",motion:"moveByPage",motionArgs:{forward:!1}},{keys:"",type:"motion",motion:"moveByScroll",motionArgs:{forward:!0,explicitRepeat:!0}},{keys:"",type:"motion",motion:"moveByScroll",motionArgs:{forward:!1,explicitRepeat:!0}},{keys:"gg",type:"motion",motion:"moveToLineOrEdgeOfDocument",motionArgs:{forward:!1,explicitRepeat:!0,linewise:!0,toJumplist:!0}},{keys:"G",type:"motion",motion:"moveToLineOrEdgeOfDocument",motionArgs:{forward:!0,explicitRepeat:!0,linewise:!0,toJumplist:!0}},{keys:"0",type:"motion",motion:"moveToStartOfLine"},{keys:"^",type:"motion",motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:"+",type:"motion",motion:"moveByLines",motionArgs:{forward:!0,toFirstChar:!0}},{keys:"-",type:"motion",motion:"moveByLines",motionArgs:{forward:!1,toFirstChar:!0}},{keys:"_",type:"motion",motion:"moveByLines",motionArgs:{forward:!0,toFirstChar:!0,repeatOffset:-1}},{keys:"$",type:"motion",motion:"moveToEol",motionArgs:{inclusive:!0}},{keys:"%",type:"motion",motion:"moveToMatchedSymbol",motionArgs:{inclusive:!0,toJumplist:!0}},{keys:"f",type:"motion",motion:"moveToCharacter",motionArgs:{forward:!0,inclusive:!0}},{keys:"F",type:"motion",motion:"moveToCharacter",motionArgs:{forward:!1}},{keys:"t",type:"motion",motion:"moveTillCharacter",motionArgs:{forward:!0,inclusive:!0}},{keys:"T",type:"motion",motion:"moveTillCharacter",motionArgs:{forward:!1}},{keys:";",type:"motion",motion:"repeatLastCharacterSearch",motionArgs:{forward:!0}},{keys:",",type:"motion",motion:"repeatLastCharacterSearch",motionArgs:{forward:!1}},{keys:"'",type:"motion",motion:"goToMark",motionArgs:{toJumplist:!0,linewise:!0}},{keys:"`",type:"motion",motion:"goToMark",motionArgs:{toJumplist:!0}},{keys:"]`",type:"motion",motion:"jumpToMark",motionArgs:{forward:!0}},{keys:"[`",type:"motion",motion:"jumpToMark",motionArgs:{forward:!1}},{keys:"]'",type:"motion",motion:"jumpToMark",motionArgs:{forward:!0,linewise:!0}},{keys:"['",type:"motion",motion:"jumpToMark",motionArgs:{forward:!1,linewise:!0}},{keys:"]p",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!0,isEdit:!0,matchIndent:!0}},{keys:"[p",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!1,isEdit:!0,matchIndent:!0}},{keys:"]",type:"motion",motion:"moveToSymbol",motionArgs:{forward:!0,toJumplist:!0}},{keys:"[",type:"motion",motion:"moveToSymbol",motionArgs:{forward:!1,toJumplist:!0}},{keys:"|",type:"motion",motion:"moveToColumn"},{keys:"o",type:"motion",motion:"moveToOtherHighlightedEnd",context:"visual"},{keys:"O",type:"motion",motion:"moveToOtherHighlightedEnd",motionArgs:{sameLine:!0},context:"visual"},{keys:"d",type:"operator",operator:"delete"},{keys:"y",type:"operator",operator:"yank"},{keys:"c",type:"operator",operator:"change"},{keys:">",type:"operator",operator:"indent",operatorArgs:{indentRight:!0}},{keys:"<",type:"operator",operator:"indent",operatorArgs:{indentRight:!1}},{keys:"g~",type:"operator",operator:"changeCase"},{keys:"gu",type:"operator",operator:"changeCase",operatorArgs:{toLower:!0},isEdit:!0},{keys:"gU",type:"operator",operator:"changeCase",operatorArgs:{toLower:!1},isEdit:!0},{keys:"n",type:"motion",motion:"findNext",motionArgs:{forward:!0,toJumplist:!0}},{keys:"N",type:"motion",motion:"findNext",motionArgs:{forward:!1,toJumplist:!0}},{keys:"x",type:"operatorMotion",operator:"delete",motion:"moveByCharacters",motionArgs:{forward:!0},operatorMotionArgs:{visualLine:!1}},{keys:"X",type:"operatorMotion",operator:"delete",motion:"moveByCharacters",motionArgs:{forward:!1},operatorMotionArgs:{visualLine:!0}},{keys:"D",type:"operatorMotion",operator:"delete",motion:"moveToEol",motionArgs:{inclusive:!0},context:"normal"},{keys:"D",type:"operator",operator:"delete",operatorArgs:{linewise:!0},context:"visual"},{keys:"Y",type:"operatorMotion",operator:"yank",motion:"moveToEol",motionArgs:{inclusive:!0},context:"normal"},{keys:"Y",type:"operator",operator:"yank",operatorArgs:{linewise:!0},context:"visual"},{keys:"C",type:"operatorMotion",operator:"change",motion:"moveToEol",motionArgs:{inclusive:!0},context:"normal"},{keys:"C",type:"operator",operator:"change",operatorArgs:{linewise:!0},context:"visual"},{keys:"~",type:"operatorMotion",operator:"changeCase",motion:"moveByCharacters",motionArgs:{forward:!0},operatorArgs:{shouldMoveCursor:!0},context:"normal"},{keys:"~",type:"operator",operator:"changeCase",context:"visual"},{keys:"",type:"operatorMotion",operator:"delete",motion:"moveByWords",motionArgs:{forward:!1,wordEnd:!1},context:"insert"},{keys:"",type:"action",action:"jumpListWalk",actionArgs:{forward:!0}},{keys:"",type:"action",action:"jumpListWalk",actionArgs:{forward:!1}},{keys:"",type:"action",action:"scroll",actionArgs:{forward:!0,linewise:!0}},{keys:"",type:"action",action:"scroll",actionArgs:{forward:!1,linewise:!0}},{keys:"a",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"charAfter"},context:"normal"},{keys:"A",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"eol"},context:"normal"},{keys:"A",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"endOfSelectedArea"},context:"visual"},{keys:"i",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"inplace"},context:"normal"},{keys:"I",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"firstNonBlank"},context:"normal"},{keys:"I",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{insertAt:"startOfSelectedArea"},context:"visual"},{keys:"o",type:"action",action:"newLineAndEnterInsertMode",isEdit:!0,interlaceInsertRepeat:!0,actionArgs:{after:!0},context:"normal"},{keys:"O",type:"action",action:"newLineAndEnterInsertMode",isEdit:!0,interlaceInsertRepeat:!0,actionArgs:{after:!1},context:"normal"},{keys:"v",type:"action",action:"toggleVisualMode"},{keys:"V",type:"action",action:"toggleVisualMode",actionArgs:{linewise:!0}},{keys:"",type:"action",action:"toggleVisualMode",actionArgs:{blockwise:!0}},{keys:"",type:"action",action:"toggleVisualMode",actionArgs:{blockwise:!0}},{keys:"gv",type:"action",action:"reselectLastSelection"},{keys:"J",type:"action",action:"joinLines",isEdit:!0},{keys:"p",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!0,isEdit:!0}},{keys:"P",type:"action",action:"paste",isEdit:!0,actionArgs:{after:!1,isEdit:!0}},{keys:"r",type:"action",action:"replace",isEdit:!0},{keys:"@",type:"action",action:"replayMacro"},{keys:"q",type:"action",action:"enterMacroRecordMode"},{keys:"R",type:"action",action:"enterInsertMode",isEdit:!0,actionArgs:{replace:!0}},{keys:"u",type:"action",action:"undo",context:"normal"},{keys:"u",type:"operator",operator:"changeCase",operatorArgs:{toLower:!0},context:"visual",isEdit:!0},{keys:"U",type:"operator",operator:"changeCase",operatorArgs:{toLower:!1},context:"visual",isEdit:!0},{keys:"",type:"action",action:"redo"},{keys:"m",type:"action",action:"setMark"},{keys:'"',type:"action",action:"setRegister"},{keys:"zz",type:"action",action:"scrollToCursor",actionArgs:{position:"center"}},{keys:"z.",type:"action",action:"scrollToCursor",actionArgs:{position:"center"},motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:"zt",type:"action",action:"scrollToCursor",actionArgs:{position:"top"}},{keys:"z",type:"action",action:"scrollToCursor",actionArgs:{position:"top"},motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:"z-",type:"action",action:"scrollToCursor",actionArgs:{position:"bottom"}},{keys:"zb",type:"action",action:"scrollToCursor",actionArgs:{position:"bottom"},motion:"moveToFirstNonWhiteSpaceCharacter"},{keys:".",type:"action",action:"repeatLastEdit"},{keys:"",type:"action",action:"incrementNumberToken",isEdit:!0,actionArgs:{increase:!0,backtrack:!1}},{keys:"",type:"action",action:"incrementNumberToken",isEdit:!0,actionArgs:{increase:!1,backtrack:!1}},{keys:"a",type:"motion",motion:"textObjectManipulation"},{keys:"i",type:"motion",motion:"textObjectManipulation",motionArgs:{textObjectInner:!0}},{keys:"/",type:"search",searchArgs:{forward:!0,querySrc:"prompt",toJumplist:!0}},{keys:"?",type:"search",searchArgs:{forward:!1,querySrc:"prompt",toJumplist:!0}},{keys:"*",type:"search",searchArgs:{forward:!0,querySrc:"wordUnderCursor",wholeWordOnly:!0,toJumplist:!0}},{keys:"#",type:"search",searchArgs:{forward:!1,querySrc:"wordUnderCursor",wholeWordOnly:!0,toJumplist:!0}},{keys:"g*",type:"search",searchArgs:{forward:!0,querySrc:"wordUnderCursor",toJumplist:!0}},{keys:"g#",type:"search",searchArgs:{forward:!1,querySrc:"wordUnderCursor",toJumplist:!0}},{keys:":",type:"ex"}],w=[{name:"colorscheme",shortName:"colo"},{name:"map"},{name:"imap",shortName:"im"},{name:"nmap",shortName:"nm"},{name:"vmap",shortName:"vm"},{name:"unmap"},{name:"write",shortName:"w"},{name:"undo",shortName:"u"},{name:"redo",shortName:"red"},{name:"set",shortName:"se"},{name:"set",shortName:"se"},{name:"setlocal",shortName:"setl"},{name:"setglobal",shortName:"setg"},{name:"sort",shortName:"sor"},{name:"substitute",shortName:"s",possiblyAsync:!0},{name:"nohlsearch",shortName:"noh"},{name:"delmarks",shortName:"delm"},{name:"registers",shortName:"reg",excludeFromCommandHistory:!0},{name:"global",shortName:"g"}],E=v.Pos,S=function(){return st};v.defineOption("vimMode",!1,function(e,t,n){t&&e.getOption("keyMap")!="vim"?e.setOption("keyMap","vim"):!t&&n!=v.Init&&/^vim/.test(e.getOption("keyMap"))&&e.setOption("keyMap","default")});var L={Shift:"S",Ctrl:"C",Alt:"A",Cmd:"D",Mod:"A"},A={Enter:"CR",Backspace:"BS",Delete:"Del"},_=/[\d]/,D=[v.isWordChar,function(e){return e&&!v.isWordChar(e)&&!/\s/.test(e)}],P=[function(e){return/\S/.test(e)}],B=H(65,26),j=H(97,26),F=H(48,10),I=[].concat(B,j,F,["<",">"]),q=[].concat(B,j,F,["-",'"',".",":","/"]),J={};K("filetype",undefined,"string",["ft"],function(e,t){if(t===undefined)return;if(e===undefined){var n=t.getOption("mode");return n=="null"?"":n}var n=e==""?"null":e;t.setOption("mode",n)});var Y=function(){function s(s,o,u){function l(n){var r=++t%e,o=i[r];o&&o.clear(),i[r]=s.setBookmark(n)}var a=t%e,f=i[a];if(f){var c=f.find();c&&!At(c,o)&&l(o)}else l(o);l(u),n=t,r=t-e+1,r<0&&(r=0)}function o(s,o){t+=o,t>n?t=n:t0?1:-1,f,l=s.getCursor();do{t+=a,u=i[(e+t)%e];if(u&&(f=u.find())&&!At(l,f))break}while(tr)}return u}var e=100,t=-1,n=0,r=0,i=new Array(e);return{cachedCursor:undefined,add:s,move:o}},Z=function(e){return e?{changes:e.changes,expectCursorActivityForChange:e.expectCursorActivityForChange}:{changes:[],expectCursorActivityForChange:!1}};et.prototype={exitMacroRecordMode:function(){var e=nt.macroModeState;e.onRecordingDone&&e.onRecordingDone(),e.onRecordingDone=undefined,e.isRecording=!1},enterMacroRecordMode:function(e,t){var n=nt.registerController.getRegister(t);n&&(n.clear(),this.latestRegister=t,e.openDialog&&(this.onRecordingDone=e.openDialog("(recording)["+t+"]",null,{bottom:!0})),this.isRecording=!0)}};var nt,it,st={buildKeyMap:function(){},getRegisterController:function(){return nt.registerController},resetVimGlobalState_:rt,getVimGlobalState_:function(){return nt},maybeInitVimState_:tt,suppressErrorLogging:!1,InsertModeKey:Gn,map:function(e,t,n){Fn.map(e,t,n)},unmap:function(e,t){Fn.unmap(e,t)},setOption:Q,getOption:G,defineOption:K,defineEx:function(e,t,n){if(!t)t=e;else if(e.indexOf(t)!==0)throw new Error('(Vim.defineEx) "'+t+'" is not a prefix of "'+e+'", command not registered');jn[e]=n,Fn.commandMap_[t]={name:e,shortName:t,type:"api"}},handleKey:function(e,t,n){var r=this.findKey(e,t,n);if(typeof r=="function")return r()},findKey:function(e,t,n){function i(){var r=nt.macroModeState;if(r.isRecording){if(t=="q")return r.exitMacroRecordMode(),ut(e),!0;n!="mapping"&&Wn(r,t)}}function s(){if(t=="")return ut(e),r.visualMode?$t(e):r.insertMode&&qn(e),!0}function o(n){var r;while(n)r=/<\w+-.+?>|<\w+>|./.exec(n),t=r[0],n=n.substring(r.index+t.length),v.Vim.handleKey(e,t,"mapping")}function u(){if(s())return!0;var n=r.inputState.keyBuffer=r.inputState.keyBuffer+t,i=t.length==1,o=ht.matchCommand(n,b,r.inputState,"insert");while(n.length>1&&o.type!="full"){var n=r.inputState.keyBuffer=n.slice(1),u=ht.matchCommand(n,b,r.inputState,"insert");u.type!="none"&&(o=u)}if(o.type=="none")return ut(e),!1;if(o.type=="partial")return it&&window.clearTimeout(it),it=window.setTimeout(function(){r.insertMode&&r.inputState.keyBuffer&&ut(e)},G("insertModeEscKeysTimeout")),!i;it&&window.clearTimeout(it);if(i){var a=e.listSelections();for(var f=0;f0||this.motionRepeat.length>0)e=1,this.prefixRepeat.length>0&&(e*=parseInt(this.prefixRepeat.join(""),10)),this.motionRepeat.length>0&&(e*=parseInt(this.motionRepeat.join(""),10));return e},at.prototype={setText:function(e,t,n){this.keyBuffer=[e||""],this.linewise=!!t,this.blockwise=!!n},pushText:function(e,t){t&&(this.linewise||this.keyBuffer.push("\n"),this.linewise=!0),this.keyBuffer.push(e)},pushInsertModeChanges:function(e){this.insertModeChanges.push(Z(e))},pushSearchQuery:function(e){this.searchQueries.push(e)},clear:function(){this.keyBuffer=[],this.insertModeChanges=[],this.searchQueries=[],this.linewise=!1},toString:function(){return this.keyBuffer.join("")}},lt.prototype={pushText:function(e,t,n,r,i){r&&n.charAt(0)=="\n"&&(n=n.slice(1)+"\n"),r&&n.charAt(n.length-1)!=="\n"&&(n+="\n");var s=this.isValidRegister(e)?this.getRegister(e):null;if(!s){switch(t){case"yank":this.registers[0]=new at(n,r,i);break;case"delete":case"change":n.indexOf("\n")==-1?this.registers["-"]=new at(n,r):(this.shiftNumericRegisters_(),this.registers[1]=new at(n,r))}this.unnamedRegister.setText(n,r,i);return}var o=X(e);o?s.pushText(n,r):s.setText(n,r,i),this.unnamedRegister.setText(s.toString(),r)},getRegister:function(e){return this.isValidRegister(e)?(e=e.toLowerCase(),this.registers[e]||(this.registers[e]=new at),this.registers[e]):this.unnamedRegister},isValidRegister:function(e){return e&&$(e,q)},shiftNumericRegisters_:function(){for(var e=9;e>=2;e--)this.registers[e]=this.getRegister(""+(e-1))}},ct.prototype={nextMatch:function(e,t){var n=this.historyBuffer,r=t?-1:1;this.initialPrefix===null&&(this.initialPrefix=e);for(var i=this.iterator+r;t?i>=0:i=n.length)return this.iterator=n.length,this.initialPrefix;if(i<0)return e},pushInput:function(e){var t=this.historyBuffer.indexOf(e);t>-1&&this.historyBuffer.splice(t,1),e.length&&this.historyBuffer.push(e)},reset:function(){this.initialPrefix=null,this.iterator=this.historyBuffer.length}};var ht={matchCommand:function(e,t,n,r){var i=Tt(e,t,r,n);if(!i.full&&!i.partial)return{type:"none"};if(!i.full&&i.partial)return{type:"partial"};var s;for(var o=0;o"&&(n.selectedCharacter=Ct(e)),{type:"full",command:s}},processCommand:function(e,t,n){t.inputState.repeatOverride=n.repeatOverride;switch(n.type){case"motion":this.processMotion(e,t,n);break;case"operator":this.processOperator(e,t,n);break;case"operatorMotion":this.processOperatorMotion(e,t,n);break;case"action":this.processAction(e,t,n);break;case"search":this.processSearch(e,t,n);break;case"ex":case"keyToEx":this.processEx(e,t,n);break;default:}},processMotion:function(e,t,n){t.inputState.motion=n.motion,t.inputState.motionArgs=Et(n.motionArgs),this.evalInput(e,t)},processOperator:function(e,t,n){var r=t.inputState;if(r.operator){if(r.operator==n.operator){r.motion="expandToLine",r.motionArgs={linewise:!0},this.evalInput(e,t);return}ut(e)}r.operator=n.operator,r.operatorArgs=Et(n.operatorArgs),t.visualMode&&this.evalInput(e,t)},processOperatorMotion:function(e,t,n){var r=t.visualMode,i=Et(n.operatorMotionArgs);i&&r&&i.visualLine&&(t.visualLine=!0),this.processOperator(e,t,n),r||this.processMotion(e,t,n)},processAction:function(e,t,n){var r=t.inputState,i=r.getRepeat(),s=!!i,o=Et(n.actionArgs)||{};r.selectedCharacter&&(o.selectedCharacter=r.selectedCharacter),n.operator&&this.processOperator(e,t,n),n.motion&&this.processMotion(e,t,n),(n.motion||n.operator)&&this.evalInput(e,t),o.repeat=i||1,o.repeatIsExplicit=s,o.registerName=r.registerName,ut(e),t.lastMotion=null,n.isEdit&&this.recordLastEdit(t,r,n),yt[n.action](e,o,t)},processSearch:function(e,t,n){function a(r,i,s){nt.searchHistoryController.pushInput(r),nt.searchHistoryController.reset();try{An(e,r,i,s)}catch(o){Tn(e,"Invalid regex: "+r),ut(e);return}ht.processMotion(e,t,{type:"motion",motion:"findNext",motionArgs:{forward:!0,toJumplist:n.searchArgs.toJumplist}})}function f(t){e.scrollTo(u.left,u.top),a(t,!0,!0);var n=nt.macroModeState;n.isRecording&&Vn(n,t)}function l(t,n,i){var s=v.keyName(t),o;s=="Up"||s=="Down"?(o=s=="Up"?!0:!1,n=nt.searchHistoryController.nextMatch(n,o)||"",i(n)):s!="Left"&&s!="Right"&&s!="Ctrl"&&s!="Alt"&&s!="Shift"&&nt.searchHistoryController.reset();var a;try{a=An(e,n,!0,!0)}catch(t){}a?e.scrollIntoView(_n(e,!r,a),30):(Dn(e),e.scrollTo(u.left,u.top))}function c(t,n,r){var i=v.keyName(t);i=="Esc"||i=="Ctrl-C"||i=="Ctrl-["||i=="Backspace"&&n==""?(nt.searchHistoryController.pushInput(n),nt.searchHistoryController.reset(),An(e,o),Dn(e),e.scrollTo(u.left,u.top),v.e_stop(t),ut(e),r(),e.focus()):i=="Ctrl-U"&&(v.e_stop(t),r(""))}if(!e.getSearchCursor)return;var r=n.searchArgs.forward,i=n.searchArgs.wholeWordOnly;dn(e).setReversed(!r);var s=r?"/":"?",o=dn(e).getQuery(),u=e.getScrollInfo();switch(n.searchArgs.querySrc){case"prompt":var h=nt.macroModeState;if(h.isPlaying){var p=h.replaySearchQueries.shift();a(p,!0,!1)}else kn(e,{onClose:f,prefix:s,desc:Cn,onKeyUp:l,onKeyDown:c});break;case"wordUnderCursor":var d=Gt(e,!1,!0,!1,!0),m=!0;d||(d=Gt(e,!1,!0,!1,!1),m=!1);if(!d)return;var p=e.getLine(d.start.line).substring(d.start.ch,d.end.ch);m&&i?p="\\b"+p+"\\b":p=Bt(p),nt.jumpList.cachedCursor=e.getCursor(),e.setCursor(d.start),a(p,!0,!1)}},processEx:function(e,t,n){function r(t){nt.exCommandHistoryController.pushInput(t),nt.exCommandHistoryController.reset(),Fn.processCommand(e,t)}function i(t,n,r){var i=v.keyName(t),s;if(i=="Esc"||i=="Ctrl-C"||i=="Ctrl-["||i=="Backspace"&&n=="")nt.exCommandHistoryController.pushInput(n),nt.exCommandHistoryController.reset(),v.e_stop(t),ut(e),r(),e.focus();i=="Up"||i=="Down"?(s=i=="Up"?!0:!1,n=nt.exCommandHistoryController.nextMatch(n,s)||"",r(n)):i=="Ctrl-U"?(v.e_stop(t),r("")):i!="Left"&&i!="Right"&&i!="Ctrl"&&i!="Alt"&&i!="Shift"&&nt.exCommandHistoryController.reset()}n.type=="keyToEx"?Fn.processCommand(e,n.exArgs.input):t.visualMode?kn(e,{onClose:r,prefix:":",value:"'<,'>",onKeyDown:i}):kn(e,{onClose:r,prefix:":",onKeyDown:i})},evalInput:function(e,t){var n=t.inputState,r=n.motion,i=n.motionArgs||{},s=n.operator,o=n.operatorArgs||{},u=n.registerName,a=t.sel,f=Lt(t.visualMode?wt(e,a.head):e.getCursor("head")),l=Lt(t.visualMode?wt(e,a.anchor):e.getCursor("anchor")),c=Lt(f),h=Lt(l),p,d,v;s&&this.recordLastEdit(t,n),n.repeatOverride!==undefined?v=n.repeatOverride:v=n.getRepeat();if(v>0&&i.explicitRepeat)i.repeatIsExplicit=!0;else if(i.noRepeat||!i.explicitRepeat&&v===0)v=1,i.repeatIsExplicit=!1;n.selectedCharacter&&(i.selectedCharacter=o.selectedCharacter=n.selectedCharacter),i.repeat=v,ut(e);if(r){var m=pt[r](e,f,i,t);t.lastMotion=pt[r];if(!m)return;if(i.toJumplist){!s&&e.ace.curOp!=null&&(e.ace.curOp.command.scrollIntoView="center-animate");var g=nt.jumpList,y=g.cachedCursor;y?(Yt(e,y,m),delete g.cachedCursor):Yt(e,f,m)}m instanceof Array?(d=m[0],p=m[1]):p=m,p||(p=Lt(f));if(t.visualMode){if(!t.visualBlock||p.ch!==Infinity)p=wt(e,p,t.visualBlock);d&&(d=wt(e,d,!0)),d=d||h,a.anchor=d,a.head=p,Wt(e),an(e,t,"<",Ot(d,p)?d:p),an(e,t,">",Ot(d,p)?p:d)}else s||(p=wt(e,p),e.setCursor(p.line,p.ch))}if(s){if(o.lastSel){d=h;var b=o.lastSel,w=Math.abs(b.head.line-b.anchor.line),S=Math.abs(b.head.ch-b.anchor.ch);b.visualLine?p=E(h.line+w,h.ch):b.visualBlock?p=E(h.line+w,h.ch+S):b.head.line==b.anchor.line?p=E(h.line,h.ch+S):p=E(h.line+w,h.ch),t.visualMode=!0,t.visualLine=b.visualLine,t.visualBlock=b.visualBlock,a=t.sel={anchor:d,head:p},Wt(e)}else t.visualMode&&(o.lastSel={anchor:Lt(a.anchor),head:Lt(a.head),visualBlock:t.visualBlock,visualLine:t.visualLine});var x,T,N,C,k;if(t.visualMode){x=Mt(a.head,a.anchor),T=_t(a.head,a.anchor),N=t.visualLine||o.linewise,C=t.visualBlock?"block":N?"line":"char",k=Xt(e,{anchor:x,head:T},C);if(N){var L=k.ranges;if(C=="block")for(var A=0;Af&&i.line==f)return;var l=e.ace.session.getFoldLine(u);return l&&(n.forward?u>l.start.row&&(u=l.end.row+1):u=l.start.row),n.toFirstChar&&(s=Qt(e.getLine(u)),r.lastHPos=s),r.lastHSPos=e.charCoords(E(u,s),"div").left,E(u,s)},moveByDisplayLines:function(e,t,n,r){var i=t;switch(r.lastMotion){case this.moveByDisplayLines:case this.moveByScroll:case this.moveByLines:case this.moveToColumn:case this.moveToEol:break;default:r.lastHSPos=e.charCoords(i,"div").left}var s=n.repeat,o=e.findPosV(i,n.forward?s:-s,"line",r.lastHSPos);if(o.hitSide)if(n.forward)var u=e.charCoords(o,"div"),a={top:u.top+8,left:r.lastHSPos},o=e.coordsChar(a,"div");else{var f=e.charCoords(E(e.firstLine(),0),"div");f.left=r.lastHSPos,o=e.coordsChar(f,"div")}return r.lastHPos=o.ch,o},moveByPage:function(e,t,n){var r=t,i=n.repeat;return e.findPosV(r,n.forward?i:-i,"page")},moveByParagraph:function(e,t,n){var r=n.forward?1:-1;return ln(e,t,n.repeat,r)},moveByScroll:function(e,t,n,r){var i=e.getScrollInfo(),s=null,o=n.repeat;o||(o=i.clientHeight/(2*e.defaultTextHeight()));var u=e.charCoords(t,"local");n.repeat=o;var s=pt.moveByDisplayLines(e,t,n,r);if(!s)return null;var a=e.charCoords(s,"local");return e.scrollTo(null,i.top+a.top-u.top),s},moveByWords:function(e,t,n){return sn(e,t,n.repeat,!!n.forward,!!n.wordEnd,!!n.bigWord)},moveTillCharacter:function(e,t,n){var r=n.repeat,i=on(e,r,n.forward,n.selectedCharacter),s=n.forward?-1:1;return Zt(s,n),i?(i.ch+=s,i):null},moveToCharacter:function(e,t,n){var r=n.repeat;return Zt(0,n),on(e,r,n.forward,n.selectedCharacter)||t},moveToSymbol:function(e,t,n){var r=n.repeat;return nn(e,r,n.forward,n.selectedCharacter)||t},moveToColumn:function(e,t,n,r){var i=n.repeat;return r.lastHPos=i-1,r.lastHSPos=e.charCoords(t,"div").left,un(e,i)},moveToEol:function(e,t,n,r){var i=t;r.lastHPos=Infinity;var s=E(i.line+n.repeat-1,Infinity),o=e.clipPos(s);return o.ch--,r.lastHSPos=e.charCoords(o,"div").left,s},moveToFirstNonWhiteSpaceCharacter:function(e,t){var n=t;return E(n.line,Qt(e.getLine(n.line)))},moveToMatchedSymbol:function(e,t){var n=t,r=n.line,i=n.ch,s=e.getLine(r),o;do{o=s.charAt(i++);if(o&&z(o)){var u=e.getTokenTypeAt(E(r,i));if(u!=="string"&&u!=="comment")break}}while(o);if(o){var a=e.findMatchingBracket(E(r,i));return a.to}return n},moveToStartOfLine:function(e,t){return E(t.line,0)},moveToLineOrEdgeOfDocument:function(e,t,n){var r=n.forward?e.lastLine():e.firstLine();return n.repeatIsExplicit&&(r=n.repeat-e.getOption("firstLineNumber")),E(r,Qt(e.getLine(r)))},textObjectManipulation:function(e,t,n,r){var i={"(":")",")":"(","{":"}","}":"{","[":"]","]":"["},s={"'":!0,'"':!0},o=n.selectedCharacter;o=="b"?o="(":o=="B"&&(o="{");var u=!n.textObjectInner,a;if(i[o])a=cn(e,t,o,u);else if(s[o])a=hn(e,t,o,u);else if(o==="W")a=Gt(e,u,!0,!0);else if(o==="w")a=Gt(e,u,!0,!1);else{if(o!=="p")return null;a=ln(e,t,n.repeat,0,u),n.linewise=!0;if(r.visualMode)r.visualLine||(r.visualLine=!0);else{var f=r.inputState.operatorArgs;f&&(f.linewise=!0),a.end.line--}}return e.state.vim.visualMode?zt(e,a.start,a.end):[a.start,a.end]},repeatLastCharacterSearch:function(e,t,n){var r=nt.lastChararacterSearch,i=n.repeat,s=n.forward===r.forward,o=(r.increment?1:0)*(s?-1:1);e.moveH(-o,"char"),n.inclusive=s?!0:!1;var u=on(e,i,s,r.selectedCharacter);return u?(u.ch+=o,u):(e.moveH(o,"char"),t)}},mt={change:function(e,t,n){var r,i,s=e.state.vim;nt.macroModeState.lastInsertModeChanges.inVisualBlock=s.visualBlock;if(!s.visualMode){var o=n[0].anchor,u=n[0].head;i=e.getRange(o,u);var a=s.lastEditInputState||{};if(a.motion=="moveByWords"&&!V(i)){var f=/\s+$/.exec(i);f&&a.motionArgs&&a.motionArgs.forward&&(u=St(u,0,-f[0].length),i=i.slice(0,-f[0].length))}var l=new E(o.line-1,Number.MAX_VALUE),c=e.firstLine()==e.lastLine();u.line>e.lastLine()&&t.linewise&&!c?e.replaceRange("",l,u):e.replaceRange("",o,u),t.linewise&&(c||(e.setCursor(l),v.commands.newlineAndIndent(e)),o.ch=Number.MAX_VALUE),r=o}else{i=e.getSelection();var h=vt("",n.length);e.replaceSelections(h),r=Mt(n[0].head,n[0].anchor)}nt.registerController.pushText(t.registerName,"change",i,t.linewise,n.length>1),yt.enterInsertMode(e,{head:r},e.state.vim)},"delete":function(e,t,n){var r,i,s=e.state.vim;if(!s.visualBlock){var o=n[0].anchor,u=n[0].head;t.linewise&&u.line!=e.firstLine()&&o.line==e.lastLine()&&o.line==u.line-1&&(o.line==e.firstLine()?o.ch=0:o=E(o.line-1,Pt(e,o.line-1))),i=e.getRange(o,u),e.replaceRange("",o,u),r=o,t.linewise&&(r=pt.moveToFirstNonWhiteSpaceCharacter(e,o))}else{i=e.getSelection();var a=vt("",n.length);e.replaceSelections(a),r=n[0].anchor}return nt.registerController.pushText(t.registerName,"delete",i,t.linewise,s.visualBlock),wt(e,r)},indent:function(e,t,n){var r=e.state.vim,i=n[0].anchor.line,s=r.visualBlock?n[n.length-1].anchor.line:n[0].head.line,o=r.visualMode?t.repeat:1;t.linewise&&s--;for(var u=i;u<=s;u++)for(var a=0;af.top?(a.line+=(u-f.top)/i,a.line=Math.ceil(a.line),e.setCursor(a),f=e.charCoords(a,"local"),e.scrollTo(null,f.top)):e.scrollTo(null,u);else{var l=u+e.getScrollInfo().clientHeight;l=i.anchor.line?s=St(i.head,0,1):s=E(i.anchor.line,0);else if(r=="inplace"&&n.visualMode)return;e.setOption("keyMap","vim-insert"),e.setOption("disableInput",!1),t&&t.replace?(e.toggleOverwrite(!0),e.setOption("keyMap","vim-replace"),v.signal(e,"vim-mode-change",{mode:"replace"})):(e.setOption("keyMap","vim-insert"),v.signal(e,"vim-mode-change",{mode:"insert"})),nt.macroModeState.isPlaying||(e.on("change",$n),v.on(e.getInputField(),"keydown",Yn)),n.visualMode&&$t(e),It(e,s,o)},toggleVisualMode:function(e,t,n){var r=t.repeat,i=e.getCursor(),s;n.visualMode?n.visualLine^t.linewise||n.visualBlock^t.blockwise?(n.visualLine=!!t.linewise,n.visualBlock=!!t.blockwise,v.signal(e,"vim-mode-change",{mode:"visual",subMode:n.visualLine?"linewise":n.visualBlock?"blockwise":""}),Wt(e)):$t(e):(n.visualMode=!0,n.visualLine=!!t.linewise,n.visualBlock=!!t.blockwise,s=wt(e,E(i.line,i.ch+r-1),!0),n.sel={anchor:i,head:s},v.signal(e,"vim-mode-change",{mode:"visual",subMode:n.visualLine?"linewise":n.visualBlock?"blockwise":""}),Wt(e),an(e,n,"<",Mt(i,s)),an(e,n,">",_t(i,s)))},reselectLastSelection:function(e,t,n){var r=n.lastSelection;n.visualMode&&Ut(e,n);if(r){var i=r.anchorMark.find(),s=r.headMark.find();if(!i||!s)return;n.sel={anchor:i,head:s},n.visualMode=!0,n.visualLine=r.visualLine,n.visualBlock=r.visualBlock,Wt(e),an(e,n,"<",Mt(i,s)),an(e,n,">",_t(i,s)),v.signal(e,"vim-mode-change",{mode:"visual",subMode:n.visualLine?"linewise":n.visualBlock?"blockwise":""})}},joinLines:function(e,t,n){var r,i;if(n.visualMode){r=e.getCursor("anchor"),i=e.getCursor("head");if(Ot(i,r)){var s=i;i=r,r=s}i.ch=Pt(e,i.line)-1}else{var o=Math.max(t.repeat,2);r=e.getCursor(),i=wt(e,E(r.line+o-1,Infinity))}var u=0;for(var a=r.line;a1)var s=Array(t.repeat+1).join(s);var p=i.linewise,d=i.blockwise;if(p&&!d)n.visualMode?s=n.visualLine?s.slice(0,-1):"\n"+s.slice(0,s.length-1)+"\n":t.after?(s="\n"+s.slice(0,s.length-1),r.ch=Pt(e,r.line)):r.ch=0;else{if(d){s=s.split("\n");for(var v=0;ve.lastLine()&&e.replaceRange("\n",E(C,0));var k=Pt(e,C);ka.length&&(s=a.length),o=E(i.line,s)}if(r=="\n")n.visualMode||e.replaceRange("",i,o),(v.commands.newlineAndIndentContinueComment||v.commands.newlineAndIndent)(e);else{var f=e.getRange(i,o);f=f.replace(/[^\n]/g,r);if(n.visualBlock){var l=(new Array(e.getOption("tabSize")+1)).join(" ");f=e.getSelection(),f=f.replace(/\t/g,l).replace(/[^\n]/g,r).split("\n"),e.replaceSelections(f)}else e.replaceRange(f,i,o);n.visualMode?(i=Ot(u[0].anchor,u[0].head)?u[0].anchor:u[0].head,e.setCursor(i),$t(e,!1)):e.setCursor(St(o,0,-1))}},incrementNumberToken:function(e,t){var n=e.getCursor(),r=e.getLine(n.line),i=/-?\d+/g,s,o,u,a,f;while((s=i.exec(r))!==null){f=s[0],o=s.index,u=o+f.length;if(n.ch=1)return!0}else e.nextCh===e.reverseSymb&&e.depth--;return!1}},section:{init:function(e){e.curMoveThrough=!0,e.symb=(e.forward?"]":"[")===e.symb?"{":"}"},isComplete:function(e){return e.index===0&&e.nextCh===e.symb}},comment:{isComplete:function(e){var t=e.lastCh==="*"&&e.nextCh==="/";return e.lastCh=e.nextCh,t}},method:{init:function(e){e.symb=e.symb==="m"?"{":"}",e.reverseSymb=e.symb==="{"?"}":"{"},isComplete:function(e){return e.nextCh===e.symb?!0:!1}},preprocess:{init:function(e){e.index=0},isComplete:function(e){if(e.nextCh==="#"){var t=e.lineText.match(/#(\w+)/)[1];if(t==="endif"){if(e.forward&&e.depth===0)return!0;e.depth++}else if(t==="if"){if(!e.forward&&e.depth===0)return!0;e.depth--}if(t==="else"&&e.depth===0)return!0}return!1}}};K("pcre",!0,"boolean"),pn.prototype={getQuery:function(){return nt.query},setQuery:function(e){nt.query=e},getOverlay:function(){return this.searchOverlay},setOverlay:function(e){this.searchOverlay=e},isReversed:function(){return nt.isReversed},setReversed:function(e){nt.isReversed=e},getScrollbarAnnotate:function(){return this.annotate},setScrollbarAnnotate:function(e){this.annotate=e}};var bn={"\\n":"\n","\\r":"\r","\\t":" "},En={"\\/":"/","\\\\":"\\","\\n":"\n","\\r":"\r","\\t":" "},Cn="(Javascript regexp)",Bn=function(){this.buildCommandMap_()};Bn.prototype={processCommand:function(e,t,n){var r=this;e.operation(function(){e.curOp.isVimOp=!0,r._processCommand(e,t,n)})},_processCommand:function(e,t,n){var r=e.state.vim,i=nt.registerController.getRegister(":"),s=i.toString();r.visualMode&&$t(e);var o=new v.StringStream(t);i.setText(t);var u=n||{};u.input=t;try{this.parseInput_(e,o,u)}catch(a){throw Tn(e,a),a}var f,l;if(!u.commandName)u.line!==undefined&&(l="move");else{f=this.matchCommand_(u.commandName);if(f){l=f.name,f.excludeFromCommandHistory&&i.setText(s),this.parseCommandArgs_(o,u,f);if(f.type=="exToKey"){for(var c=0;c0;t--){var n=e.substring(0,t);if(this.commandMap_[n]){var r=this.commandMap_[n];if(r.name.indexOf(e)===0)return r}}return null},buildCommandMap_:function(){this.commandMap_={};for(var e=0;e
    ";if(!n)for(var s in r){var o=r[s].toString();o.length&&(i+='"'+s+" "+o+"
    ")}else{var s;n=n.join("");for(var u=0;u"}}Tn(e,i)},sort:function(e,t){function o(){if(t.argString){var e=new v.StringStream(t.argString);e.eat("!")&&(n=!0);if(e.eol())return;if(!e.eatSpace())return"Invalid arguments";var o=e.match(/[a-z]+/);if(o){o=o[0],r=o.indexOf("i")!=-1,i=o.indexOf("u")!=-1;var u=o.indexOf("d")!=-1&&1,a=o.indexOf("x")!=-1&&1,f=o.indexOf("o")!=-1&&1;if(u+a+f>1)return"Invalid arguments";s=u&&"decimal"||a&&"hex"||f&&"octal"}if(e.match(/\/.*\//))return"patterns not supported"}}function b(e,t){if(n){var i;i=e,e=t,t=i}r&&(e=e.toLowerCase(),t=t.toLowerCase());var o=s&&p.exec(e),u=s&&p.exec(t);return o?(o=parseInt((o[1]+o[2]).toLowerCase(),d),u=parseInt((u[1]+u[2]).toLowerCase(),d),o-u):e")}if(!u){Tn(e,c);return}var d=0,v=function(){if(d=f){Tn(e,"Invalid argument: "+t.argString.substring(i));return}for(var l=0;l<=f-a;l++){var c=String.fromCharCode(a+l);delete n.marks[c]}}else delete n.marks[s]}}},Fn=new Bn;v.keyMap.vim={attach:C,detach:N,call:k},K("insertModeEscKeysTimeout",200,"number"),v.keyMap["vim-insert"]={"Ctrl-N":"autocomplete","Ctrl-P":"autocomplete",Enter:function(e){var t=v.commands.newlineAndIndentContinueComment||v.commands.newlineAndIndent;t(e)},fallthrough:["default"],attach:C,detach:N,call:k},v.keyMap["vim-replace"]={Backspace:"goCharLeft",fallthrough:["vim-insert"],attach:C,detach:N,call:k},rt(),v.Vim=S(),S=v.Vim;var tr={"return":"CR",backspace:"BS","delete":"Del",esc:"Esc",left:"Left",right:"Right",up:"Up",down:"Down",space:"Space",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"CR"},rr=S.handleKey.bind(S);S.handleKey=function(e,t,n){return e.operation(function(){return rr(e,t,n)},!0)},t.CodeMirror=v;var or=S.maybeInitVimState_;t.handler={$id:"ace/keyboard/vim",drawCursor:function(e,t,n,r,s){var o=this.state.vim||{},u=n.characterWidth,a=n.lineHeight,f=t.top,l=t.left;if(!o.insertMode){var c=r.cursor?i.comparePoints(r.cursor,r.start)<=0:s.selection.isBackwards()||s.selection.isEmpty();!c&&l>u&&(l-=u)}!o.insertMode&&o.status&&(a/=2,f+=a),e.left=l+"px",e.top=f+"px",e.width=u+"px",e.height=a+"px"},handleKeyboard:function(e,t,n,r,i){var s=e.editor,o=s.state.cm,u=or(o);if(r==-1)return;if(n=="c"&&t==1){if(!c.isMac&&s.getCopyText())return s.once("copy",function(){s.selection.clearSelection()}),{command:"null",passEvent:!0}}else u.insertMode||c.isMac&&this.handleMacRepeat(e,t,n)&&(t=-1,n=e.inputChar);if(t==-1||t&1||t===0&&n.length>1){var a=u.insertMode,f=nr(t,n,i||{});u.status==null&&(u.status="");var l=sr(o,f,"user");u=or(o),l&&u.status!=null?u.status+=f:u.status==null&&(u.status=""),o._signal("changeStatus");if(!l&&(t!=-1||a))return;return{command:"null",passEvent:!l}}},attach:function(e){e.state||(e.state={});var t=new v(e);e.state.cm=t,e.$vimModeHandler=this,v.keyMap.vim.attach(t),or(t).status=null,t.on("vim-command-done",function(){if(t.virtualSelectionMode())return;or(t).status=null,t.ace._signal("changeStatus"),t.ace.session.markUndoGroup()}),t.on("changeStatus",function(){t.ace.renderer.updateCursor(),t.ace._signal("changeStatus")}),t.on("vim-mode-change",function(){if(t.virtualSelectionMode())return;t.ace.renderer.setStyle("normal-mode",!or(t).insertMode),t._signal("changeStatus")}),t.ace.renderer.setStyle("normal-mode",!or(t).insertMode),e.renderer.$cursorLayer.drawCursor=this.drawCursor.bind(t),this.updateMacCompositionHandlers(e,!0)},detach:function(e){var t=e.state.cm;v.keyMap.vim.detach(t),t.destroy(),e.state.cm=null,e.$vimModeHandler=null,e.renderer.$cursorLayer.drawCursor=null,e.renderer.setStyle("normal-mode",!1),this.updateMacCompositionHandlers(e,!1)},getStatusText:function(e){var t=e.state.cm,n=or(t);if(n.insertMode)return"INSERT";var r="";return n.visualMode&&(r+="VISUAL",n.visualLine&&(r+=" LINE"),n.visualBlock&&(r+=" BLOCK")),n.status&&(r+=(r?" ":"")+n.status),r},handleMacRepeat:function(e,t,n){if(t==-1)e.inputChar=n,e.lastEvent="input";else if(e.inputChar&&e.$lastHash==t&&e.$lastKey==n){if(e.lastEvent=="input")e.lastEvent="input1";else if(e.lastEvent=="input1")return!0}else e.$lastHash=t,e.$lastKey=n,e.lastEvent="keypress"},updateMacCompositionHandlers:function(e,t){var n=function(t){var n=e.state.cm,r=or(n);if(!r.insertMode){var i=this.textInput.getElement();i.blur(),i.focus(),i.value=t}else this.onCompositionUpdateOrig(t)},r=function(t){var n=e.state.cm,r=or(n);r.insertMode||this.onCompositionStartOrig(t)};t?e.onCompositionUpdateOrig||(e.onCompositionUpdateOrig=e.onCompositionUpdate,e.onCompositionUpdate=n,e.onCompositionStartOrig=e.onCompositionStart,e.onCompositionStart=r):e.onCompositionUpdateOrig&&(e.onCompositionUpdate=e.onCompositionUpdateOrig,e.onCompositionUpdateOrig=null,e.onCompositionStart=e.onCompositionStartOrig,e.onCompositionStartOrig=null)}};var ur={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return e.getLength().toString().length*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update)},detach:function(e){e.renderer.$gutterLayer.$renderer=null,e.off("changeSelection",this.update)}};S.defineOption({name:"wrap",set:function(e,t){t&&t.ace.setOption("wrap",e)},type:"boolean"},!1),S.defineEx("write","w",function(){console.log(":write is not implemented")}),b.push({keys:"zc",type:"action",action:"fold",actionArgs:{open:!1}},{keys:"zC",type:"action",action:"fold",actionArgs:{open:!1,all:!0}},{keys:"zo",type:"action",action:"fold",actionArgs:{open:!0}},{keys:"zO",type:"action",action:"fold",actionArgs:{open:!0,all:!0}},{keys:"za",type:"action",action:"fold",actionArgs:{toggle:!0}},{keys:"zA",type:"action",action:"fold",actionArgs:{toggle:!0,all:!0}},{keys:"zf",type:"action",action:"fold",actionArgs:{open:!0,all:!0}},{keys:"zd",type:"action",action:"fold",actionArgs:{open:!0,all:!0}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorAbove"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorBelow"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorAboveSkipCurrent"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"addCursorBelowSkipCurrent"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectMoreBefore"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectMoreAfter"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectNextBefore"}},{keys:"",type:"action",action:"aceCommand",actionArgs:{name:"selectNextAfter"}}),yt.aceCommand=function(e,t,n){e.vimCmd=t,e.ace.inVirtualSelectionMode?e.ace.on("beforeEndOperation",ar):ar(null,e.ace)},yt.fold=function(e,t,n){e.ace.execCommand(["toggleFoldWidget","toggleFoldWidget","foldOther","unfoldall"][(t.all?2:0)+(t.open?1:0)])},t.handler.defaultKeymap=b,t.handler.actions=yt,t.Vim=S,S.map("Y","yy","normal")}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-assembly_x86.js b/public/themes/pterodactyl/vendor/ace/mode-assembly_x86.js deleted file mode 100644 index 1ed120aac..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-assembly_x86.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/assembly_x86_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"keyword.control.assembly",regex:"\\b(?:aaa|aad|aam|aas|adc|add|addpd|addps|addsd|addss|addsubpd|addsubps|aesdec|aesdeclast|aesenc|aesenclast|aesimc|aeskeygenassist|and|andpd|andps|andnpd|andnps|arpl|blendpd|blendps|blendvpd|blendvps|bound|bsf|bsr|bswap|bt|btc|btr|bts|cbw|cwde|cdqe|clc|cld|cflush|clts|cmc|cmov(?:n?e|ge?|ae?|le?|be?|n?o|n?z)|cmp|cmppd|cmpps|cmps|cnpsb|cmpsw|cmpsd|cmpsq|cmpss|cmpxchg|cmpxchg8b|cmpxchg16b|comisd|comiss|cpuid|crc32|cvtdq2pd|cvtdq2ps|cvtpd2dq|cvtpd2pi|cvtpd2ps|cvtpi2pd|cvtpi2ps|cvtps2dq|cvtps2pd|cvtps2pi|cvtsd2si|cvtsd2ss|cvts2sd|cvtsi2ss|cvtss2sd|cvtss2si|cvttpd2dq|cvtpd2pi|cvttps2dq|cvttps2pi|cvttps2dq|cvttps2pi|cvttsd2si|cvttss2si|cwd|cdq|cqo|daa|das|dec|div|divpd|divps|divsd|divss|dppd|dpps|emms|enter|extractps|f2xm1|fabs|fadd|faddp|fiadd|fbld|fbstp|fchs|fclex|fnclex|fcmov(?:n?e|ge?|ae?|le?|be?|n?o|n?z)|fcom|fcmop|fcompp|fcomi|fcomip|fucomi|fucomip|fcos|fdecstp|fdiv|fdivp|fidiv|fdivr|fdivrp|fidivr|ffree|ficom|ficomp|fild|fincstp|finit|fnint|fist|fistp|fisttp|fld|fld1|fldl2t|fldl2e|fldpi|fldlg2|fldln2|fldz|fldcw|fldenv|fmul|fmulp|fimul|fnop|fpatan|fprem|fprem1|fptan|frndint|frstor|fsave|fnsave|fscale|fsin|fsincos|fsqrt|fst|fstp|fstcw|fnstcw|fstenv|fnstenv|fsts|fnstsw|fsub|fsubp|fisub|fsubr|fsubrp|fisubr|ftst|fucom|fucomp|fucompp|fxam|fxch|fxrstor|fxsave|fxtract|fyl2x|fyl2xp1|haddpd|haddps|husbpd|hsubps|idiv|imul|in|inc|ins|insb|insw|insd|insertps|int|into|invd|invplg|invpcid|iret|iretd|iretq|lahf|lar|lddqu|ldmxcsr|lds|les|lfs|lgs|lss|lea|leave|lfence|lgdt|lidt|llgdt|lmsw|lock|lods|lodsb|lodsw|lodsd|lodsq|lsl|ltr|maskmovdqu|maskmovq|maxpd|maxps|maxsd|maxss|mfence|minpd|minps|minsd|minss|monitor|mov|movapd|movaps|movbe|movd|movq|movddup|movdqa|movdqu|movq2q|movhlps|movhpd|movhps|movlhps|movlpd|movlps|movmskpd|movmskps|movntdqa|movntdq|movnti|movntpd|movntps|movntq|movq|movq2dq|movs|movsb|movsw|movsd|movsq|movsd|movshdup|movsldup|movss|movsx|movsxd|movupd|movups|movzx|mpsadbw|mul|mulpd|mulps|mulsd|mulss|mwait|neg|not|or|orpd|orps|out|outs|outsb|outsw|outsd|pabsb|pabsw|pabsd|packsswb|packssdw|packusdw|packuswbpaddb|paddw|paddd|paddq|paddsb|paddsw|paddusb|paddusw|palignr|pand|pandn|pause|pavgb|pavgw|pblendvb|pblendw|pclmulqdq|pcmpeqb|pcmpeqw|pcmpeqd|pcmpeqq|pcmpestri|pcmpestrm|pcmptb|pcmptgw|pcmpgtd|pcmpgtq|pcmpistri|pcmpisrm|pextrb|pextrd|pextrq|pextrw|phaddw|phaddd|phaddsw|phinposuw|phsubw|phsubd|phsubsw|pinsrb|pinsrd|pinsrq|pinsrw|pmaddubsw|pmadddwd|pmaxsb|pmaxsd|pmaxsw|pmaxsw|pmaxub|pmaxud|pmaxuw|pminsb|pminsd|pminsw|pminub|pminud|pminuw|pmovmskb|pmovsx|pmovzx|pmuldq|pmulhrsw|pmulhuw|pmulhw|pmulld|pmullw|pmuludw|pop|popa|popad|popcnt|popf|popfd|popfq|por|prefetch|psadbw|pshufb|pshufd|pshufhw|pshuflw|pshufw|psignb|psignw|psignd|pslldq|psllw|pslld|psllq|psraw|psrad|psrldq|psrlw|psrld|psrlq|psubb|psubw|psubd|psubq|psubsb|psubsw|psubusb|psubusw|test|ptest|punpckhbw|punpckhwd|punpckhdq|punpckhddq|punpcklbw|punpcklwd|punpckldq|punpckldqd|push|pusha|pushad|pushf|pushfd|pxor|prcl|rcr|rol|ror|rcpps|rcpss|rdfsbase|rdgsbase|rdmsr|rdpmc|rdrand|rdtsc|rdtscp|rep|repe|repz|repne|repnz|roundpd|roundps|roundsd|roundss|rsm|rsqrps|rsqrtss|sahf|sal|sar|shl|shr|sbb|scas|scasb|scasw|scasd|set(?:n?e|ge?|ae?|le?|be?|n?o|n?z)|sfence|sgdt|shld|shrd|shufpd|shufps|sidt|sldt|smsw|sqrtpd|sqrtps|sqrtsd|sqrtss|stc|std|stmxcsr|stos|stosb|stosw|stosd|stosq|str|sub|subpd|subps|subsd|subss|swapgs|syscall|sysenter|sysexit|sysret|teset|ucomisd|ucomiss|ud2|unpckhpd|unpckhps|unpcklpd|unpcklps|vbroadcast|vcvtph2ps|vcvtp2sph|verr|verw|vextractf128|vinsertf128|vmaskmov|vpermilpd|vpermilps|vperm2f128|vtestpd|vtestps|vzeroall|vzeroupper|wait|fwait|wbinvd|wrfsbase|wrgsbase|wrmsr|xadd|xchg|xgetbv|xlat|xlatb|xor|xorpd|xorps|xrstor|xsave|xsaveopt|xsetbv|lzcnt|extrq|insertq|movntsd|movntss|vfmaddpd|vfmaddps|vfmaddsd|vfmaddss|vfmaddsubbpd|vfmaddsubps|vfmsubaddpd|vfmsubaddps|vfmsubpd|vfmsubps|vfmsubsd|vfnmaddpd|vfnmaddps|vfnmaddsd|vfnmaddss|vfnmsubpd|vfnmusbps|vfnmusbsd|vfnmusbss|cvt|xor|cli|sti|hlt|nop|lock|wait|enter|leave|ret|loop(?:n?e|n?z)?|call|j(?:mp|n?e|ge?|ae?|le?|be?|n?o|n?z))\\b",caseInsensitive:!0},{token:"variable.parameter.register.assembly",regex:"\\b(?:CS|DS|ES|FS|GS|SS|RAX|EAX|RBX|EBX|RCX|ECX|RDX|EDX|RCX|RIP|EIP|IP|RSP|ESP|SP|RSI|ESI|SI|RDI|EDI|DI|RFLAGS|EFLAGS|FLAGS|R8-15|(?:Y|X)MM(?:[0-9]|10|11|12|13|14|15)|(?:A|B|C|D)(?:X|H|L)|CR(?:[0-4]|DR(?:[0-7]|TR6|TR7|EFER)))\\b",caseInsensitive:!0},{token:"constant.character.decimal.assembly",regex:"\\b[0-9]+\\b"},{token:"constant.character.hexadecimal.assembly",regex:"\\b0x[A-F0-9]+\\b",caseInsensitive:!0},{token:"constant.character.hexadecimal.assembly",regex:"\\b[A-F0-9]+h\\b",caseInsensitive:!0},{token:"string.assembly",regex:/'([^\\']|\\.)*'/},{token:"string.assembly",regex:/"([^\\"]|\\.)*"/},{token:"support.function.directive.assembly",regex:"^\\[",push:[{token:"support.function.directive.assembly",regex:"\\]$",next:"pop"},{defaultToken:"support.function.directive.assembly"}]},{token:["support.function.directive.assembly","support.function.directive.assembly","entity.name.function.assembly"],regex:"(^struc)( )([_a-zA-Z][_a-zA-Z0-9]*)"},{token:"support.function.directive.assembly",regex:"^endstruc\\b"},{token:["support.function.directive.assembly","entity.name.function.assembly","support.function.directive.assembly","constant.character.assembly"],regex:"^(%macro )([_a-zA-Z][_a-zA-Z0-9]*)( )([0-9]+)"},{token:"support.function.directive.assembly",regex:"^%endmacro"},{token:["text","support.function.directive.assembly","text","entity.name.function.assembly"],regex:"(\\s*)(%define|%xdefine|%idefine|%undef|%assign|%defstr|%strcat|%strlen|%substr|%00|%0|%rotate|%rep|%endrep|%include|\\$\\$|\\$|%unmacro|%if|%elif|%else|%endif|%(?:el)?ifdef|%(?:el)?ifmacro|%(?:el)?ifctx|%(?:el)?ifidn|%(?:el)?ifidni|%(?:el)?ifid|%(?:el)?ifnum|%(?:el)?ifstr|%(?:el)?iftoken|%(?:el)?ifempty|%(?:el)?ifenv|%pathsearch|%depend|%use|%push|%pop|%repl|%arg|%stacksize|%local|%error|%warning|%fatal|%line|%!|%comment|%endcomment|__NASM_VERSION_ID__|__NASM_VER__|__FILE__|__LINE__|__BITS__|__OUTPUT_FORMAT__|__DATE__|__TIME__|__DATE_NUM__|_TIME__NUM__|__UTC_DATE__|__UTC_TIME__|__UTC_DATE_NUM__|__UTC_TIME_NUM__|__POSIX_TIME__|__PASS__|ISTRUC|AT|IEND|BITS 16|BITS 32|BITS 64|USE16|USE32|__SECT__|ABSOLUTE|EXTERN|GLOBAL|COMMON|CPU|FLOAT)\\b( ?)((?:[_a-zA-Z][_a-zA-Z0-9]*)?)",caseInsensitive:!0},{token:"support.function.directive.assembly",regex:"\\b(?:d[bwdqtoy]|res[bwdqto]|equ|times|align|alignb|sectalign|section|ptr|byte|word|dword|qword|incbin)\\b",caseInsensitive:!0},{token:"entity.name.function.assembly",regex:"^\\s*%%[\\w.]+?:$"},{token:"entity.name.function.assembly",regex:"^\\s*%\\$[\\w.]+?:$"},{token:"entity.name.function.assembly",regex:"^[\\w.]+?:"},{token:"entity.name.function.assembly",regex:"^[\\w.]+?\\b"},{token:"comment.assembly",regex:";.*$"}]},this.normalizeRules()};s.metaData={fileTypes:["asm"],name:"Assembly x86",scopeName:"source.assembly"},r.inherits(s,i),t.AssemblyX86HighlightRules=s}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u>=|>>>=|<>|&&|\|\||\?:|[*%\/+\-&\^|~!<>=]=?/},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],singleLineComment:[{token:"comment",regex:/\\$/,next:"singleLineComment"},{token:"comment",regex:/$/,next:"start"},{defaultToken:"comment"}],directive:[{token:"constant.other.multiline",regex:/\\/},{token:"constant.other.multiline",regex:/.*\\/},{token:"constant.other",regex:"\\s*<.+?>",next:"start"},{token:"constant.other",regex:'\\s*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]',next:"start"},{token:"constant.other",regex:"\\s*['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']",next:"start"},{token:"constant.other",regex:/[^\\\/]+/,next:"start"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(u,s),t.c_cppHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/c_cpp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/c_cpp_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./c_cpp_highlight_rules").c_cppHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var u=t.match(/^.*[\{\(\[]\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/c_cpp"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-coffee.js b/public/themes/pterodactyl/vendor/ace/mode-coffee.js deleted file mode 100644 index 82005fd2a..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-coffee.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/coffee_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function s(){var e="[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*",t="this|throw|then|try|typeof|super|switch|return|break|by|continue|catch|class|in|instanceof|is|isnt|if|else|extends|for|own|finally|function|while|when|new|no|not|delete|debugger|do|loop|of|off|or|on|unless|until|and|yes",n="true|false|null|undefined|NaN|Infinity",r="case|const|default|function|var|void|with|enum|export|implements|interface|let|package|private|protected|public|static|yield",i="Array|Boolean|Date|Function|Number|Object|RegExp|ReferenceError|String|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray",s="Math|JSON|isNaN|isFinite|parseInt|parseFloat|encodeURI|encodeURIComponent|decodeURI|decodeURIComponent|String|",o="window|arguments|prototype|document",u=this.createKeywordMapper({keyword:t,"constant.language":n,"invalid.illegal":r,"language.support.class":i,"language.support.function":s,"variable.language":o},"identifier"),a={token:["paren.lparen","variable.parameter","paren.rparen","text","storage.type"],regex:/(?:(\()((?:"[^")]*?"|'[^')]*?'|\/[^\/)]*?\/|[^()"'\/])*?)(\))(\s*))?([\-=]>)/.source},f=/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)/;this.$rules={start:[{token:"constant.numeric",regex:"(?:0x[\\da-fA-F]+|(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:[eE][+-]?\\d+)?)"},{stateName:"qdoc",token:"string",regex:"'''",next:[{token:"string",regex:"'''",next:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"qqdoc",token:"string",regex:'"""',next:[{token:"string",regex:'"""',next:"start"},{token:"paren.string",regex:"#{",push:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"qstring",token:"string",regex:"'",next:[{token:"string",regex:"'",next:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"qqstring",token:"string.start",regex:'"',next:[{token:"string.end",regex:'"',next:"start"},{token:"paren.string",regex:"#{",push:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{stateName:"js",token:"string",regex:"`",next:[{token:"string",regex:"`",next:"start"},{token:"constant.language.escape",regex:f},{defaultToken:"string"}]},{regex:"[{}]",onMatch:function(e,t,n){this.next="";if(e=="{"&&n.length)return n.unshift("start",t),"paren";if(e=="}"&&n.length){n.shift(),this.next=n.shift()||"";if(this.next.indexOf("string")!=-1)return"paren.string"}return"paren"}},{token:"string.regex",regex:"///",next:"heregex"},{token:"string.regex",regex:/(?:\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)(?:[imgy]{0,4})(?!\w)/},{token:"comment",regex:"###(?!#)",next:"comment"},{token:"comment",regex:"#.*"},{token:["punctuation.operator","text","identifier"],regex:"(\\.)(\\s*)("+r+")"},{token:"punctuation.operator",regex:"\\.{1,3}"},{token:["keyword","text","language.support.class","text","keyword","text","language.support.class"],regex:"(class)(\\s+)("+e+")(?:(\\s+)(extends)(\\s+)("+e+"))?"},{token:["entity.name.function","text","keyword.operator","text"].concat(a.token),regex:"("+e+")(\\s*)([=:])(\\s*)"+a.regex},a,{token:"variable",regex:"@(?:"+e+")?"},{token:u,regex:e},{token:"punctuation.operator",regex:"\\,|\\."},{token:"storage.type",regex:"[\\-=]>"},{token:"keyword.operator",regex:"(?:[-+*/%<>&|^!?=]=|>>>=?|\\-\\-|\\+\\+|::|&&=|\\|\\|=|<<=|>>=|\\?\\.|\\.{2,3}|[!*+-=><])"},{token:"paren.lparen",regex:"[({[]"},{token:"paren.rparen",regex:"[\\]})]"},{token:"text",regex:"\\s+"}],heregex:[{token:"string.regex",regex:".*?///[imgy]{0,4}",next:"start"},{token:"comment.regex",regex:"\\s+(?:#.*)?"},{token:"string.regex",regex:"\\S+"}],comment:[{token:"comment",regex:"###",next:"start"},{defaultToken:"comment"}]},this.normalizeRules()}var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules;r.inherits(s,i),t.CoffeeHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u|\b(?:else|try|(?:swi|ca)tch(?:\s+[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)?|finally))\s*$|^\s*(else\b\s*)?(?:if|for|while|loop)\b(?!.*\bthen\b)/;this.lineCommentStart="#",this.blockComment={start:"###",end:"###"},this.getNextLineIndent=function(t,n,r){var i=this.$getIndent(n),s=this.getTokenizer().getLineTokens(n,t).tokens;return(!s.length||s[s.length-1].type!=="comment")&&t==="start"&&e.test(n)&&(i+=r),i},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/coffee_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/coffee"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-csharp.js b/public/themes/pterodactyl/vendor/ace/mode-csharp.js deleted file mode 100644 index 040bca703..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-csharp.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/csharp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e=this.createKeywordMapper({"variable.language":"this",keyword:"abstract|event|new|struct|as|explicit|null|switch|base|extern|object|this|bool|false|operator|throw|break|finally|out|true|byte|fixed|override|try|case|float|params|typeof|catch|for|private|uint|char|foreach|protected|ulong|checked|goto|public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|continue|in|return|using|decimal|int|sbyte|virtual|default|interface|sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|double|lock|stackalloc|else|long|static|enum|namespace|string|var|dynamic","constant.language":"null|true|false"},"identifier");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:/'(?:.|\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n]))'/},{token:"string",start:'"',end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"string",start:'@"',end:'"',next:[{token:"constant.language.escape",regex:'""'}]},{token:"string",start:/\$"/,end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?$)|{{/},{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"keyword",regex:"^\\s*#(if|else|elif|endif|define|undef|warning|error|line|region|endregion|pragma)"},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(o,s),t.CSharpHighlightRules=o}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/folding/csharp",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./cstyle").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.usingRe=/^\s*using \S/,this.getFoldWidgetRangeBase=this.getFoldWidgetRange,this.getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=this.getFoldWidgetBase(e,t,n);if(!r){var i=e.getLine(n);if(/^\s*#region\b/.test(i))return"start";var s=this.usingRe;if(s.test(i)){var o=e.getLine(n-1),u=e.getLine(n+1);if(!s.test(o)&&s.test(u))return"start"}}return r},this.getFoldWidgetRange=function(e,t,n){var r=this.getFoldWidgetRangeBase(e,t,n);if(r)return r;var i=e.getLine(n);if(this.usingRe.test(i))return this.getUsingStatementBlock(e,i,n);if(/^\s*#region\b/.test(i))return this.getRegionBlock(e,i,n)},this.getUsingStatementBlock=function(e,t,n){var r=t.match(this.usingRe)[0].length-1,s=e.getLength(),o=n,u=n;while(++no){var a=e.getLine(u).length;return new i(o,r,u,a)}},this.getRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*#(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/csharp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/csharp_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/csharp"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./csharp_highlight_rules").CSharpHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/csharp").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new u,this.foldingRules=new a};r.inherits(f,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){return null},this.$id="ace/mode/csharp"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-css.js b/public/themes/pterodactyl/vendor/ace/mode-css.js deleted file mode 100644 index b41ecdc82..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-css.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-golang.js b/public/themes/pterodactyl/vendor/ace/mode-golang.js deleted file mode 100644 index 0bec98ab8..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-golang.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/golang_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e="else|break|case|return|goto|if|const|select|continue|struct|default|switch|for|range|func|import|package|chan|defer|fallthrough|go|interface|map|range|select|type|var",t="string|uint8|uint16|uint32|uint64|int8|int16|int32|int64|float32|float64|complex64|complex128|byte|rune|uint|int|uintptr|bool|error",n="new|close|cap|copy|panic|panicln|print|println|len|make|delete|real|recover|imag|append",r="nil|true|false|iota",s=this.createKeywordMapper({keyword:e,"constant.language":r,"support.function":n,"support.type":t},""),o="\\\\(?:[0-7]{3}|x\\h{2}|u{4}|U\\h{6}|[abfnrtv'\"\\\\])".replace(/\\h/g,"[a-fA-F\\d]");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment.start",regex:"\\/\\*",next:"comment"},{token:"string",regex:/"(?:[^"\\]|\\.)*?"/},{token:"string",regex:"`",next:"bqstring"},{token:"constant.numeric",regex:"'(?:[^\\'\ud800-\udbff]|[\ud800-\udbff][\udc00-\udfff]|"+o.replace('"',"")+")'"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:["keyword","text","entity.name.function"],regex:"(func)(\\s+)([a-zA-Z_$][a-zA-Z0-9_$]*)\\b"},{token:function(e){return e[e.length-1]=="("?[{type:s(e.slice(0,-1))||"support.function",value:e.slice(0,-1)},{type:"paren.lparen",value:e.slice(-1)}]:s(e)||"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b\\(?"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|==|=|!=|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^="},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment.end",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}],bqstring:[{token:"string",regex:"`",next:"start"},{defaultToken:"string"}]},this.embedRules(i,"doc-",[i.getEndRule("start")])};r.inherits(o,s),t.GolangHighlightRules=o}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/golang",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/golang_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("./golang_highlight_rules").GolangHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/cstyle").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new a,this.$behaviour=new u};r.inherits(f,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var u=t.match(/^.*[\{\(\[]\s*$/);u&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/golang"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-haml.js b/public/themes/pterodactyl/vendor/ace/mode-haml.js deleted file mode 100644 index 23eee53df..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-haml.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/ruby_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.constantOtherSymbol={token:"constant.other.symbol.ruby",regex:"[:](?:[A-Za-z_]|[@$](?=[a-zA-Z0-9_]))[a-zA-Z0-9_]*[!=?]?"},o=t.qString={token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},u=t.qqString={token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},a=t.tString={token:"string",regex:"[`](?:(?:\\\\.)|(?:[^'\\\\]))*?[`]"},f=t.constantNumericHex={token:"constant.numeric",regex:"0[xX][0-9a-fA-F](?:[0-9a-fA-F]|_(?=[0-9a-fA-F]))*\\b"},l=t.constantNumericFloat={token:"constant.numeric",regex:"[+-]?\\d(?:\\d|_(?=\\d))*(?:(?:\\.\\d(?:\\d|_(?=\\d))*)?(?:[eE][+-]?\\d+)?)?\\b"},c=function(){var e="abort|Array|assert|assert_equal|assert_not_equal|assert_same|assert_not_same|assert_nil|assert_not_nil|assert_match|assert_no_match|assert_in_delta|assert_throws|assert_raise|assert_nothing_raised|assert_instance_of|assert_kind_of|assert_respond_to|assert_operator|assert_send|assert_difference|assert_no_difference|assert_recognizes|assert_generates|assert_response|assert_redirected_to|assert_template|assert_select|assert_select_email|assert_select_rjs|assert_select_encoded|css_select|at_exit|attr|attr_writer|attr_reader|attr_accessor|attr_accessible|autoload|binding|block_given?|callcc|caller|catch|chomp|chomp!|chop|chop!|defined?|delete_via_redirect|eval|exec|exit|exit!|fail|Float|flunk|follow_redirect!|fork|form_for|form_tag|format|gets|global_variables|gsub|gsub!|get_via_redirect|host!|https?|https!|include|Integer|lambda|link_to|link_to_unless_current|link_to_function|link_to_remote|load|local_variables|loop|open|open_session|p|print|printf|proc|putc|puts|post_via_redirect|put_via_redirect|raise|rand|raw|readline|readlines|redirect?|request_via_redirect|require|scan|select|set_trace_func|sleep|split|sprintf|srand|String|stylesheet_link_tag|syscall|system|sub|sub!|test|throw|trace_var|trap|untrace_var|atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan|render|javascript_include_tag|csrf_meta_tag|label_tag|text_field_tag|submit_tag|check_box_tag|content_tag|radio_button_tag|text_area_tag|password_field_tag|hidden_field_tag|fields_for|select_tag|options_for_select|options_from_collection_for_select|collection_select|time_zone_select|select_date|select_time|select_datetime|date_select|time_select|datetime_select|select_year|select_month|select_day|select_hour|select_minute|select_second|file_field_tag|file_field|respond_to|skip_before_filter|around_filter|after_filter|verify|protect_from_forgery|rescue_from|helper_method|redirect_to|before_filter|send_data|send_file|validates_presence_of|validates_uniqueness_of|validates_length_of|validates_format_of|validates_acceptance_of|validates_associated|validates_exclusion_of|validates_inclusion_of|validates_numericality_of|validates_with|validates_each|authenticate_or_request_with_http_basic|authenticate_or_request_with_http_digest|filter_parameter_logging|match|get|post|resources|redirect|scope|assert_routing|translate|localize|extract_locale_from_tld|caches_page|expire_page|caches_action|expire_action|cache|expire_fragment|expire_cache_for|observe|cache_sweeper|has_many|has_one|belongs_to|has_and_belongs_to_many",t="alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|__FILE__|finally|for|gem|if|in|__LINE__|module|next|not|or|private|protected|public|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield",n="true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING",r="$DEBUG|$defout|$FILENAME|$LOAD_PATH|$SAFE|$stdin|$stdout|$stderr|$VERBOSE|$!|root_url|flash|session|cookies|params|request|response|logger|self",i=this.$keywords=this.createKeywordMapper({keyword:t,"constant.language":n,"variable.language":r,"support.function":e,"invalid.deprecated":"debugger"},"identifier");this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"comment",regex:"^=begin(?:$|\\s.*$)",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},[{regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)return n.unshift("start",t),"paren.lparen";if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1)return"paren.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.start",regex:/"/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/"/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/`/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/`/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/'/,push:[{token:"constant.language.escape",regex:/\\['\\]/},{token:"string.end",regex:/'/,next:"pop"},{defaultToken:"string"}]}],{token:"text",regex:"::"},{token:"variable.instance",regex:"@{1,2}[a-zA-Z_\\d]+"},{token:"support.class",regex:"[A-Z][a-zA-Z_\\d]+"},s,f,l,{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"punctuation.separator.key-value",regex:"=>"},{stateName:"heredoc",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[3]),[{type:"constant",value:i[1]},{type:"string",value:i[2]},{type:"support.class",value:i[3]},{type:"string",value:i[4]}]},regex:"(<<-?)(['\"`]?)([\\w]+)(['\"`]?)",rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:"string.character",regex:"\\B\\?."},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"^=end(?:$|\\s.*$)",next:"start"},{token:"comment",regex:".+"}]},this.normalizeRules()};r.inherits(c,i),t.RubyHighlightRules=c}),define("ace/mode/haml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/html_highlight_rules","ace/mode/ruby_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html_highlight_rules").HtmlHighlightRules,s=e("./ruby_highlight_rules"),o=s.RubyHighlightRules,u=function(){i.call(this),this.$rules.start.unshift({token:"punctuation.section.comment",regex:/^\s*\/.*/},{token:"string.quoted.double",regex:"==.+?=="},{token:"keyword.other.doctype",regex:"^!!!\\s*(?:[a-zA-Z0-9-_]+)?"},s.qString,s.qqString,s.tString,{token:"character.escape.haml",regex:"^\\s*\\\\."},{token:"text",regex:/^\s*/,next:"tag_single"},s.constantNumericHex,s.constantNumericFloat,s.constantOtherSymbol,{token:"text",regex:"=|-|~",next:"embedded_ruby"}),this.$rules.tag_single=[{token:"meta.tag.haml",regex:/(%[\w:\-]+)/},{token:"keyword.attribute-name.class.haml",regex:"\\.[\\w-]+"},{token:"keyword.attribute-name.id.haml",regex:"#[\\w-]+"},{token:"punctuation.section",regex:"\\{",next:"section"},s.constantOtherSymbol,{token:"text",regex:/\s/,next:"start"},{token:"empty",regex:"$|(?!\\.|#|\\{|\\[|=|-|~|\\/)",next:"start"}],this.$rules.section=[s.constantOtherSymbol,s.qString,s.qqString,s.tString,s.constantNumericHex,s.constantNumericFloat,{token:"punctuation.section",regex:"\\}",next:"start"}],this.$rules.embedded_ruby=[s.constantNumericHex,s.constantNumericFloat,{token:"support.class",regex:"[A-Z][a-zA-Z_\\d]+"},{token:(new o).getKeywords(),regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:["keyword","text","text"],regex:"(?:do|\\{)(?: \\|[^|]+\\|)?$",next:"start"},{token:["text"],regex:"^$",next:"start"},{token:["text"],regex:"^(?!.*\\|\\s*$)",next:"start"}],this.normalizeRules()};r.inherits(u,i),t.HamlHighlightRules=u}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-ini.js b/public/themes/pterodactyl/vendor/ace/mode-ini.js deleted file mode 100644 index 3826b3cd0..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-ini.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/ini_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s="\\\\(?:[\\\\0abtrn;#=:]|x[a-fA-F\\d]{4})",o=function(){this.$rules={start:[{token:"punctuation.definition.comment.ini",regex:"#.*",push_:[{token:"comment.line.number-sign.ini",regex:"$|^",next:"pop"},{defaultToken:"comment.line.number-sign.ini"}]},{token:"punctuation.definition.comment.ini",regex:";.*",push_:[{token:"comment.line.semicolon.ini",regex:"$|^",next:"pop"},{defaultToken:"comment.line.semicolon.ini"}]},{token:["keyword.other.definition.ini","text","punctuation.separator.key-value.ini"],regex:"\\b([a-zA-Z0-9_.-]+)\\b(\\s*)(=)"},{token:["punctuation.definition.entity.ini","constant.section.group-title.ini","punctuation.definition.entity.ini"],regex:"^(\\[)(.*?)(\\])"},{token:"punctuation.definition.string.begin.ini",regex:"'",push:[{token:"punctuation.definition.string.end.ini",regex:"'",next:"pop"},{token:"constant.language.escape",regex:s},{defaultToken:"string.quoted.single.ini"}]},{token:"punctuation.definition.string.begin.ini",regex:'"',push:[{token:"constant.language.escape",regex:s},{token:"punctuation.definition.string.end.ini",regex:'"',next:"pop"},{defaultToken:"string.quoted.double.ini"}]}]},this.normalizeRules()};o.metaData={fileTypes:["ini","conf"],keyEquivalent:"^~I",name:"Ini",scopeName:"source.ini"},r.inherits(o,i),t.IniHighlightRules=o}),define("ace/mode/folding/ini",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(){};r.inherits(o,s),function(){this.foldingStartMarker=/^\s*\[([^\])]*)]\s*(?:$|[;#])/,this.getFoldWidgetRange=function(e,t,n){var r=this.foldingStartMarker,s=e.getLine(n),o=s.match(r);if(!o)return;var u=o[1]+".",a=s.length,f=e.getLength(),l=n,c=n;while(++nl){var h=e.getLine(c).length;return new i(l,a,c,h)}}}.call(o.prototype)}),define("ace/mode/ini",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/ini_highlight_rules","ace/mode/folding/ini"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./ini_highlight_rules").IniHighlightRules,o=e("./folding/ini").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart=";",this.blockComment=null,this.$id="ace/mode/ini"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-java.js b/public/themes/pterodactyl/vendor/ace/mode-java.js deleted file mode 100644 index cdddb9bb6..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-java.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/java_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e="abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while",t="null|Infinity|NaN|undefined",n="AbstractMethodError|AssertionError|ClassCircularityError|ClassFormatError|Deprecated|EnumConstantNotPresentException|ExceptionInInitializerError|IllegalAccessError|IllegalThreadStateException|InstantiationError|InternalError|NegativeArraySizeException|NoSuchFieldError|Override|Process|ProcessBuilder|SecurityManager|StringIndexOutOfBoundsException|SuppressWarnings|TypeNotPresentException|UnknownError|UnsatisfiedLinkError|UnsupportedClassVersionError|VerifyError|InstantiationException|IndexOutOfBoundsException|ArrayIndexOutOfBoundsException|CloneNotSupportedException|NoSuchFieldException|IllegalArgumentException|NumberFormatException|SecurityException|Void|InheritableThreadLocal|IllegalStateException|InterruptedException|NoSuchMethodException|IllegalAccessException|UnsupportedOperationException|Enum|StrictMath|Package|Compiler|Readable|Runtime|StringBuilder|Math|IncompatibleClassChangeError|NoSuchMethodError|ThreadLocal|RuntimePermission|ArithmeticException|NullPointerException|Long|Integer|Short|Byte|Double|Number|Float|Character|Boolean|StackTraceElement|Appendable|StringBuffer|Iterable|ThreadGroup|Runnable|Thread|IllegalMonitorStateException|StackOverflowError|OutOfMemoryError|VirtualMachineError|ArrayStoreException|ClassCastException|LinkageError|NoClassDefFoundError|ClassNotFoundException|RuntimeException|Exception|ThreadDeath|Error|Throwable|System|ClassLoader|Cloneable|Class|CharSequence|Comparable|String|Object",r=this.createKeywordMapper({"variable.language":"this",keyword:e,"constant.language":t,"support.function":n},"identifier");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F][0-9a-fA-F_]*|[bB][01][01_]*)[LlSsDdFfYy]?\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.[\d_]*)?(?:[eE][+-]?[\d_]+)?)?[LlSsDdFfYy]?\b/},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(i,"doc-",[i.getEndRule("start")])};r.inherits(o,s),t.JavaHighlightRules=o}),define("ace/mode/java",["require","exports","module","ace/lib/oop","ace/mode/javascript","ace/mode/java_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./javascript").Mode,s=e("./java_highlight_rules").JavaHighlightRules,o=function(){i.call(this),this.HighlightRules=s};r.inherits(o,i),function(){this.createWorker=function(e){return null},this.$id="ace/mode/java"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-javascript.js b/public/themes/pterodactyl/vendor/ace/mode-javascript.js deleted file mode 100644 index 08939125f..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-javascript.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-json.js b/public/themes/pterodactyl/vendor/ace/mode-json.js deleted file mode 100644 index c919855a6..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-json.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"variable",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)'},{token:"string",regex:'"',next:"string"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:"invalid.illegal",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"invalid.illegal",regex:"\\/\\/.*$"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],string:[{token:"constant.language.escape",regex:/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:'"',next:"start"},{token:"string",regex:"",next:"start"}]}};r.inherits(s,i),t.JsonHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./json_highlight_rules").JsonHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/cstyle").FoldMode,f=e("../worker/worker_client").WorkerClient,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new u,this.foldingRules=new a};r.inherits(l,i),function(){this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/json_worker","JsonWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/json"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-kotlin.js b/public/themes/pterodactyl/vendor/ace/mode-kotlin.js deleted file mode 100644 index 462cc3444..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-kotlin.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/kotlin_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{include:"#comments"},{token:["text","keyword.other.kotlin","text","entity.name.package.kotlin","text"],regex:/^(\s*)(package)\b(?:(\s*)([^ ;$]+)(\s*))?/},{include:"#imports"},{include:"#statements"}],"#classes":[{token:"text",regex:/(?=\s*(?:companion|class|object|interface))/,push:[{token:"text",regex:/}|(?=$)/,next:"pop"},{token:["keyword.other.kotlin","text"],regex:/\b((?:companion\s*)?)(class|object|interface)\b/,push:[{token:"text",regex:/(?=<|{|\(|:)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\bobject\b/},{token:"entity.name.type.class.kotlin",regex:/\w+/}]},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#parameters"}]},{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?={|$)/,next:"pop"},{token:"entity.other.inherited-class.kotlin",regex:/\w+/},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#expressions"}]}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#statements"}]}]}],"#comments":[{token:"punctuation.definition.comment.kotlin",regex:/\/\*/,push:[{token:"punctuation.definition.comment.kotlin",regex:/\*\//,next:"pop"},{defaultToken:"comment.block.kotlin"}]},{token:["text","punctuation.definition.comment.kotlin","comment.line.double-slash.kotlin"],regex:/(\s*)(\/\/)(.*$)/}],"#constants":[{token:"constant.language.kotlin",regex:/\b(?:true|false|null|this|super)\b/},{token:"constant.numeric.kotlin",regex:/\b(?:0(?:x|X)[0-9a-fA-F]*|(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:(?:e|E)(?:\+|-)?[0-9]+)?)(?:[LlFfUuDd]|UL|ul)?\b/},{token:"constant.other.kotlin",regex:/\b[A-Z][A-Z0-9_]+\b/}],"#expressions":[{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#expressions"}]},{include:"#types"},{include:"#strings"},{include:"#constants"},{include:"#comments"},{include:"#keywords"}],"#functions":[{token:"text",regex:/(?=\s*fun)/,push:[{token:"text",regex:/}|(?=$)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\bfun\b/,push:[{token:"text",regex:/(?=\()/,next:"pop"},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{token:["text","entity.name.function.kotlin"],regex:/((?:[\.<\?>\w]+\.)?)(\w+)/}]},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#parameters"}]},{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?={|=|$)/,next:"pop"},{include:"#types"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/(?=\})/,next:"pop"},{include:"#statements"}]},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$)/,next:"pop"},{include:"#expressions"}]}]}],"#generics":[{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?=,|>)/,next:"pop"},{include:"#types"}]},{include:"#keywords"},{token:"storage.type.generic.kotlin",regex:/\w+/}],"#getters-and-setters":[{token:["entity.name.function.kotlin","text"],regex:/\b(get)\b(\s*\(\s*\))/,push:[{token:"text",regex:/\}|(?=\bset\b)|$/,next:"pop"},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$|\bset\b)/,next:"pop"},{include:"#expressions"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#expressions"}]}]},{token:["entity.name.function.kotlin","text"],regex:/\b(set)\b(\s*)(?=\()/,push:[{token:"text",regex:/\}|(?=\bget\b)|$/,next:"pop"},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#parameters"}]},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$|\bset\b)/,next:"pop"},{include:"#expressions"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#expressions"}]}]}],"#imports":[{token:["text","keyword.other.kotlin","text","keyword.other.kotlin"],regex:/^(\s*)(import)(\s+[^ $]+\s+)((?:as)?)/}],"#keywords":[{token:"storage.modifier.kotlin",regex:/\b(?:var|val|public|private|protected|abstract|final|enum|open|attribute|annotation|override|inline|var|val|vararg|lazy|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof)\b/},{token:"keyword.control.catch-exception.kotlin",regex:/\b(?:try|catch|finally|throw)\b/},{token:"keyword.control.kotlin",regex:/\b(?:if|else|while|for|do|return|when|where|break|continue)\b/},{token:"keyword.operator.kotlin",regex:/\b(?:in|is|as|assert)\b/},{token:"keyword.operator.comparison.kotlin",regex:/==|!=|===|!==|<=|>=|<|>/},{token:"keyword.operator.assignment.kotlin",regex:/=/},{token:"keyword.operator.declaration.kotlin",regex:/:/},{token:"keyword.operator.dot.kotlin",regex:/\./},{token:"keyword.operator.increment-decrement.kotlin",regex:/\-\-|\+\+/},{token:"keyword.operator.arithmetic.kotlin",regex:/\-|\+|\*|\/|%/},{token:"keyword.operator.arithmetic.assign.kotlin",regex:/\+=|\-=|\*=|\/=/},{token:"keyword.operator.logical.kotlin",regex:/!|&&|\|\|/},{token:"keyword.operator.range.kotlin",regex:/\.\./},{token:"punctuation.terminator.kotlin",regex:/;/}],"#namespaces":[{token:"keyword.other.kotlin",regex:/\bnamespace\b/},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#statements"}]}],"#parameters":[{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?=,|\)|=)/,next:"pop"},{include:"#types"}]},{token:"keyword.operator.declaration.kotlin",regex:/=/,push:[{token:"text",regex:/(?=,|\))/,next:"pop"},{include:"#expressions"}]},{include:"#keywords"},{token:"variable.parameter.function.kotlin",regex:/\w+/}],"#statements":[{include:"#namespaces"},{include:"#typedefs"},{include:"#classes"},{include:"#functions"},{include:"#variables"},{include:"#getters-and-setters"},{include:"#expressions"}],"#strings":[{token:"punctuation.definition.string.begin.kotlin",regex:/"""/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/"""/,next:"pop"},{token:"variable.parameter.template.kotlin",regex:/\$\w+|\$\{[^\}]+\}/},{token:"constant.character.escape.kotlin",regex:/\\./},{defaultToken:"string.quoted.third.kotlin"}]},{token:"punctuation.definition.string.begin.kotlin",regex:/"/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/"/,next:"pop"},{token:"variable.parameter.template.kotlin",regex:/\$\w+|\$\{[^\}]+\}/},{token:"constant.character.escape.kotlin",regex:/\\./},{defaultToken:"string.quoted.double.kotlin"}]},{token:"punctuation.definition.string.begin.kotlin",regex:/'/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/'/,next:"pop"},{token:"constant.character.escape.kotlin",regex:/\\./},{defaultToken:"string.quoted.single.kotlin"}]},{token:"punctuation.definition.string.begin.kotlin",regex:/`/,push:[{token:"punctuation.definition.string.end.kotlin",regex:/`/,next:"pop"},{defaultToken:"string.quoted.single.kotlin"}]}],"#typedefs":[{token:"text",regex:/(?=\s*type)/,push:[{token:"text",regex:/(?=$)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\btype\b/},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{include:"#expressions"}]}],"#types":[{token:"storage.type.buildin.kotlin",regex:/\b(?:Any|Unit|String|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b/},{token:"storage.type.buildin.array.kotlin",regex:/\b(?:IntArray|BooleanArray|CharArray|LongArray|DoubleArray|FloatArray|ShortArray|ByteArray)\b/},{token:["storage.type.buildin.collection.kotlin","text"],regex:/\b(Array|List|Map)(<\b)/,push:[{token:"text",regex:/>/,next:"pop"},{include:"#types"},{include:"#keywords"}]},{token:"text",regex:/\w+/,next:"pop"},{include:"#types"},{include:"#keywords"}]},{token:["keyword.operator.tuple.kotlin","text"],regex:/(#)(\()/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#expressions"}]},{token:"text",regex:/\{/,push:[{token:"text",regex:/\}/,next:"pop"},{include:"#statements"}]},{token:"text",regex:/\(/,push:[{token:"text",regex:/\)/,next:"pop"},{include:"#types"}]},{token:"keyword.operator.declaration.kotlin",regex:/->/}],"#variables":[{token:"text",regex:/(?=\s*(?:var|val))/,push:[{token:"text",regex:/(?=:|=|$)/,next:"pop"},{token:"keyword.other.kotlin",regex:/\b(?:var|val)\b/,push:[{token:"text",regex:/(?=:|=|$)/,next:"pop"},{token:"text",regex://,next:"pop"},{include:"#generics"}]},{token:["text","entity.name.variable.kotlin"],regex:/((?:[\.<\?>\w]+\.)?)(\w+)/}]},{token:"keyword.operator.declaration.kotlin",regex:/:/,push:[{token:"text",regex:/(?==|$)/,next:"pop"},{include:"#types"},{include:"#getters-and-setters"}]},{token:"keyword.operator.assignment.kotlin",regex:/=/,push:[{token:"text",regex:/(?=$)/,next:"pop"},{include:"#expressions"},{include:"#getters-and-setters"}]}]}]},this.normalizeRules()};s.metaData={fileTypes:["kt","kts"],name:"Kotlin",scopeName:"source.Kotlin"},r.inherits(s,i),t.KotlinHighlightRules=s}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/kotlin",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/kotlin_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./kotlin_highlight_rules").KotlinHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o};r.inherits(u,i),function(){this.$id="ace/mode/kotlin"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-lua.js b/public/themes/pterodactyl/vendor/ace/mode-lua.js deleted file mode 100644 index bd473efc8..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-lua.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/lua_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="break|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while|or|and|not",t="true|false|nil|_G|_VERSION",n="string|xpcall|package|tostring|print|os|unpack|require|getfenv|setmetatable|next|assert|tonumber|io|rawequal|collectgarbage|getmetatable|module|rawset|math|debug|pcall|table|newproxy|type|coroutine|_G|select|gcinfo|pairs|rawget|loadstring|ipairs|_VERSION|dofile|setfenv|load|error|loadfile|sub|upper|len|gfind|rep|find|match|char|dump|gmatch|reverse|byte|format|gsub|lower|preload|loadlib|loaded|loaders|cpath|config|path|seeall|exit|setlocale|date|getenv|difftime|remove|time|clock|tmpname|rename|execute|lines|write|close|flush|open|output|type|read|stderr|stdin|input|stdout|popen|tmpfile|log|max|acos|huge|ldexp|pi|cos|tanh|pow|deg|tan|cosh|sinh|random|randomseed|frexp|ceil|floor|rad|abs|sqrt|modf|asin|min|mod|fmod|log10|atan2|exp|sin|atan|getupvalue|debug|sethook|getmetatable|gethook|setmetatable|setlocal|traceback|setfenv|getinfo|setupvalue|getlocal|getregistry|getfenv|setn|insert|getn|foreachi|maxn|foreach|concat|sort|remove|resume|yield|status|wrap|create|running|__add|__sub|__mod|__unm|__concat|__lt|__index|__call|__gc|__metatable|__mul|__div|__pow|__len|__eq|__le|__newindex|__tostring|__mode|__tonumber",r="string|package|os|io|math|debug|table|coroutine",i="setn|foreach|foreachi|gcinfo|log10|maxn",s=this.createKeywordMapper({keyword:e,"support.function":n,"keyword.deprecated":i,"constant.library":r,"constant.language":t,"variable.language":"self"},"identifier"),o="(?:(?:[1-9]\\d*)|(?:0))",u="(?:0[xX][\\dA-Fa-f]+)",a="(?:"+o+"|"+u+")",f="(?:\\.\\d+)",l="(?:\\d+)",c="(?:(?:"+l+"?"+f+")|(?:"+l+"\\.))",h="(?:"+c+")";this.$rules={start:[{stateName:"bracketedComment",onMatch:function(e,t,n){return n.unshift(this.next,e.length-2,t),"comment"},regex:/\-\-\[=*\[/,next:[{onMatch:function(e,t,n){return e.length==n[1]?(n.shift(),n.shift(),this.next=n.shift()):this.next="","comment"},regex:/\]=*\]/,next:"start"},{defaultToken:"comment"}]},{token:"comment",regex:"\\-\\-.*$"},{stateName:"bracketedString",onMatch:function(e,t,n){return n.unshift(this.next,e.length,t),"comment"},regex:/\[=*\[/,next:[{onMatch:function(e,t,n){return e.length==n[1]?(n.shift(),n.shift(),this.next=n.shift()):this.next="","comment"},regex:/\]=*\]/,next:"start"},{defaultToken:"comment"}]},{token:"string",regex:'"(?:[^\\\\]|\\\\.)*?"'},{token:"string",regex:"'(?:[^\\\\]|\\\\.)*?'"},{token:"constant.numeric",regex:h},{token:"constant.numeric",regex:a+"\\b"},{token:s,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\/|%|\\#|\\^|~|<|>|<=|=>|==|~=|=|\\:|\\.\\.\\.|\\.\\."},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+|\\w+"}]},this.normalizeRules()};r.inherits(s,i),t.LuaHighlightRules=s}),define("ace/mode/folding/lua",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=e("../../token_iterator").TokenIterator,u=t.FoldMode=function(){};r.inherits(u,i),function(){this.foldingStartMarker=/\b(function|then|do|repeat)\b|{\s*$|(\[=*\[)/,this.foldingStopMarker=/\bend\b|^\s*}|\]=*\]/,this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=this.foldingStartMarker.test(r),s=this.foldingStopMarker.test(r);if(i&&!s){var o=r.match(this.foldingStartMarker);if(o[1]=="then"&&/\belseif\b/.test(r))return;if(o[1]){if(e.getTokenAt(n,o.index+1).type==="keyword")return"start"}else{if(!o[2])return"start";var u=e.bgTokenizer.getState(n)||"";if(u[0]=="bracketedComment"||u[0]=="bracketedString")return"start"}}if(t!="markbeginend"||!s||i&&s)return"";var o=r.match(this.foldingStopMarker);if(o[0]==="end"){if(e.getTokenAt(n,o.index+1).type==="keyword")return"end"}else{if(o[0][0]!=="]")return"end";var u=e.bgTokenizer.getState(n-1)||"";if(u[0]=="bracketedComment"||u[0]=="bracketedString")return"end"}},this.getFoldWidgetRange=function(e,t,n){var r=e.doc.getLine(n),i=this.foldingStartMarker.exec(r);if(i)return i[1]?this.luaBlock(e,n,i.index+1):i[2]?e.getCommentFoldRange(n,i.index+1):this.openingBracketBlock(e,"{",n,i.index);var i=this.foldingStopMarker.exec(r);if(i)return i[0]==="end"&&e.getTokenAt(n,i.index+1).type==="keyword"?this.luaBlock(e,n,i.index+1):i[0][0]==="]"?e.getCommentFoldRange(n,i.index+1):this.closingBracketBlock(e,"}",n,i.index+i[0].length)},this.luaBlock=function(e,t,n){var r=new o(e,t,n),i={"function":1,"do":1,then:1,elseif:-1,end:-1,repeat:1,until:-1},u=r.getCurrentToken();if(!u||u.type!="keyword")return;var a=u.value,f=[a],l=i[a];if(!l)return;var c=l===-1?r.getCurrentTokenColumn():e.getLine(t).length,h=t;r.step=l===-1?r.stepBackward:r.stepForward;while(u=r.step()){if(u.type!=="keyword")continue;var p=l*i[u.value];if(p>0)f.unshift(u.value);else if(p<=0){f.shift();if(!f.length&&u.value!="elseif")break;p===0&&f.unshift(u.value)}}var t=r.getCurrentTokenRow();return l===-1?new s(t,e.getLine(t).length,h,c):new s(h,c,t,r.getCurrentTokenColumn())}}.call(u.prototype)}),define("ace/mode/lua",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/lua_highlight_rules","ace/mode/folding/lua","ace/range","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./lua_highlight_rules").LuaHighlightRules,o=e("./folding/lua").FoldMode,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(f,i),function(){function n(t){var n=0;for(var r=0;r0?1:0}this.lineCommentStart="--",this.blockComment={start:"--[",end:"]--"};var e={"function":1,then:1,"do":1,"else":1,elseif:1,repeat:1,end:-1,until:-1},t=["else","elseif","end","until"];this.getNextLineIndent=function(e,t,r){var i=this.$getIndent(t),s=0,o=this.getTokenizer().getLineTokens(t,e),u=o.tokens;return e=="start"&&(s=n(u)),s>0?i+r:s<0&&i.substr(i.length-r.length)==r&&!this.checkOutdent(e,t,"\n")?i.substr(0,i.length-r.length):i},this.checkOutdent=function(e,n,r){if(r!="\n"&&r!="\r"&&r!="\r\n")return!1;if(n.match(/^\s*[\)\}\]]$/))return!0;var i=this.getTokenizer().getLineTokens(n.trim(),e).tokens;return!i||!i.length?!1:i[0].type=="keyword"&&t.indexOf(i[0].value)!=-1},this.autoOutdent=function(e,t,r){var i=t.getLine(r-1),s=this.$getIndent(i).length,o=this.getTokenizer().getLineTokens(i,"start").tokens,a=t.getTabString().length,f=s+a*n(o),l=this.$getIndent(t.getLine(r)).length;if(l",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column"},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/xml_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/xml"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/folding/mixed",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e,t){this.defaultMode=e,this.subModes=t};r.inherits(s,i),function(){this.$getMode=function(e){typeof e!="string"&&(e=e[0]);for(var t in this.subModes)if(e.indexOf(t)===0)return this.subModes[t];return null},this.$tryMode=function(e,t,n,r){var i=this.$getMode(e);return i?i.getFoldWidget(t,n,r):""},this.getFoldWidget=function(e,t,n){return this.$tryMode(e.getState(n-1),e,t,n)||this.$tryMode(e.getState(n),e,t,n)||this.defaultMode.getFoldWidget(e,t,n)},this.getFoldWidgetRange=function(e,t,n){var r=this.$getMode(e.getState(n-1));if(!r||!r.getFoldWidget(e,t,n))r=this.$getMode(e.getState(n));if(!r||!r.getFoldWidget(e,t,n))r=this.defaultMode;return r.getFoldWidgetRange(e,t,n)}}.call(s.prototype)}),define("ace/mode/folding/html",["require","exports","module","ace/lib/oop","ace/mode/folding/mixed","ace/mode/folding/xml","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./mixed").FoldMode,s=e("./xml").FoldMode,o=e("./cstyle").FoldMode,u=t.FoldMode=function(e,t){i.call(this,new s(e,t),{"js-":new o,"css-":new o})};r.inherits(u,i)}),define("ace/mode/html_completions",["require","exports","module","ace/token_iterator"],function(e,t,n){"use strict";function f(e,t){return e.type.lastIndexOf(t+".xml")>-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),define("ace/mode/markdown_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules","ace/mode/html_highlight_rules","ace/mode/css_highlight_rules"],function(e,t,n){"use strict";function c(e,t){return{token:"support.function",regex:"^\\s*```"+e+"\\s*$",push:t+"start"}}var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./css_highlight_rules").CssHighlightRules,l=function(e){return"(?:[^"+i.escapeRegExp(e)+"\\\\]|\\\\.)*"},h=function(){a.call(this),this.$rules.start.unshift({token:"empty_line",regex:"^$",next:"allowBlock"},{token:"markup.heading.1",regex:"^=+(?=\\s*$)"},{token:"markup.heading.2",regex:"^\\-+(?=\\s*$)"},{token:function(e){return"markup.heading."+e.length},regex:/^#{1,6}(?=\s*[^ #]|\s+#.)/,next:"header"},c("(?:javascript|js)","jscode-"),c("xml","xmlcode-"),c("html","htmlcode-"),c("css","csscode-"),{token:"support.function",regex:"^\\s*```\\s*\\S*(?:{.*?\\})?\\s*$",next:"githubblock"},{token:"string.blockquote",regex:"^\\s*>\\s*(?:[*+-]|\\d+\\.)?\\s+",next:"blockquote"},{token:"constant",regex:"^ {0,2}(?:(?: ?\\* ?){3,}|(?: ?\\- ?){3,}|(?: ?\\_ ?){3,})\\s*$",next:"allowBlock"},{token:"markup.list",regex:"^\\s{0,3}(?:[*+-]|\\d+\\.)\\s+",next:"listblock-start"},{include:"basic"}),this.addRules({basic:[{token:"constant.language.escape",regex:/\\[\\`*_{}\[\]()#+\-.!]/},{token:"support.function",regex:"(`+)(.*?[^`])(\\1)"},{token:["text","constant","text","url","string","text"],regex:'^([ ]{0,3}\\[)([^\\]]+)(\\]:\\s*)([^ ]+)(\\s*(?:["][^"]+["])?(\\s*))$'},{token:["text","string","text","constant","text"],regex:"(\\[)("+l("]")+")(\\]\\s*\\[)("+l("]")+")(\\])"},{token:["text","string","text","markup.underline","string","text"],regex:"(\\[)("+l("]")+")(\\]\\()"+'((?:[^\\)\\s\\\\]|\\\\.|\\s(?=[^"]))*)'+'(\\s*"'+l('"')+'"\\s*)?'+"(\\))"},{token:"string.strong",regex:"([*]{2}|[_]{2}(?=\\S))(.*?\\S[*_]*)(\\1)"},{token:"string.emphasis",regex:"([*]|[_](?=\\S))(.*?\\S[*_]*)(\\1)"},{token:["text","url","text"],regex:"(<)((?:https?|ftp|dict):[^'\">\\s]+|(?:mailto:)?[-.\\w]+\\@[-a-z0-9]+(?:\\.[-a-z0-9]+)*\\.[a-z]+)(>)"}],allowBlock:[{token:"support.function",regex:"^ {4}.+",next:"allowBlock"},{token:"empty_line",regex:"^$",next:"allowBlock"},{token:"empty",regex:"",next:"start"}],header:[{regex:"$",next:"start"},{include:"basic"},{defaultToken:"heading"}],"listblock-start":[{token:"support.variable",regex:/(?:\[[ x]\])?/,next:"listblock"}],listblock:[{token:"empty_line",regex:"^$",next:"start"},{token:"markup.list",regex:"^\\s{0,3}(?:[*+-]|\\d+\\.)\\s+",next:"listblock-start"},{include:"basic",noEscape:!0},{token:"support.function",regex:"^\\s*```\\s*[a-zA-Z]*(?:{.*?\\})?\\s*$",next:"githubblock"},{defaultToken:"list"}],blockquote:[{token:"empty_line",regex:"^\\s*$",next:"start"},{token:"string.blockquote",regex:"^\\s*>\\s*(?:[*+-]|\\d+\\.)?\\s+",next:"blockquote"},{include:"basic",noEscape:!0},{defaultToken:"string.blockquote"}],githubblock:[{token:"support.function",regex:"^\\s*```",next:"start"},{token:"support.function",regex:".+"}]}),this.embedRules(o,"jscode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.embedRules(a,"htmlcode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.embedRules(f,"csscode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.embedRules(u,"xmlcode-",[{token:"support.function",regex:"^\\s*```",next:"pop"}]),this.normalizeRules()};r.inherits(h,s),t.MarkdownHighlightRules=h}),define("ace/mode/folding/markdown",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.foldingStartMarker=/^(?:[=-]+\s*$|#{1,6} |`{3})/,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?r[0]=="`"?e.bgTokenizer.getState(n)=="start"?"end":"start":"start":""},this.getFoldWidgetRange=function(e,t,n){function l(t){return f=e.getTokens(t)[0],f&&f.type.lastIndexOf(c,0)===0}function h(){var e=f.value[0];return e=="="?6:e=="-"?5:7-f.value.search(/[^#]/)}var r=e.getLine(n),i=r.length,o=e.getLength(),u=n,a=n;if(!r.match(this.foldingStartMarker))return;if(r[0]=="`"){if(e.bgTokenizer.getState(n)!=="start"){while(++n0){r=e.getLine(n);if(r[0]=="`"&r.substring(0,3)=="```")break}return new s(n,r.length,u,0)}var f,c="markup.heading";if(l(n)){var p=h();while(++n=p)break}a=n-(!f||["=","-"].indexOf(f.value[0])==-1?1:2);if(a>u)while(a>u&&/^\s*$/.test(e.getLine(a)))a--;if(a>u){var v=e.getLine(a).length;return new s(u,i,a,v)}}}}.call(o.prototype)}),define("ace/mode/markdown",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript","ace/mode/xml","ace/mode/html","ace/mode/markdown_highlight_rules","ace/mode/folding/markdown"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript").Mode,o=e("./xml").Mode,u=e("./html").Mode,a=e("./markdown_highlight_rules").MarkdownHighlightRules,f=e("./folding/markdown").FoldMode,l=function(){this.HighlightRules=a,this.createModeDelegates({"js-":s,"xml-":o,"html-":u}),this.foldingRules=new f,this.$behaviour=this.$defaultBehaviour};r.inherits(l,i),function(){this.type="text",this.blockComment={start:""},this.getNextLineIndent=function(e,t,n){if(e=="listblock"){var r=/^(\s*)(?:([-+*])|(\d+)\.)(\s+)/.exec(t);if(!r)return"";var i=r[2];return i||(i=parseInt(r[3],10)+1+"."),r[1]+i+r[4]}return this.$getIndent(t)},this.$id="ace/mode/markdown"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-mysql.js b/public/themes/pterodactyl/vendor/ace/mode-mysql.js deleted file mode 100644 index b8e6a8d1b..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-mysql.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/mysql_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./doc_comment_highlight_rules").DocCommentHighlightRules,o=e("./text_highlight_rules").TextHighlightRules,u=function(){function i(e){var t=e.start,n=e.escape;return{token:"string.start",regex:t,next:[{token:"constant.language.escape",regex:n},{token:"string.end",next:"start",regex:t},{defaultToken:"string"}]}}var e="alter|and|as|asc|between|count|create|delete|desc|distinct|drop|from|having|in|insert|into|is|join|like|not|on|or|order|select|set|table|union|update|values|where|accessible|action|add|after|algorithm|all|analyze|asensitive|at|authors|auto_increment|autocommit|avg|avg_row_length|before|binary|binlog|both|btree|cache|call|cascade|cascaded|case|catalog_name|chain|change|changed|character|check|checkpoint|checksum|class_origin|client_statistics|close|coalesce|code|collate|collation|collations|column|columns|comment|commit|committed|completion|concurrent|condition|connection|consistent|constraint|contains|continue|contributors|convert|cross|current_date|current_time|current_timestamp|current_user|cursor|data|database|databases|day_hour|day_microsecond|day_minute|day_second|deallocate|dec|declare|default|delay_key_write|delayed|delimiter|des_key_file|describe|deterministic|dev_pop|dev_samp|deviance|directory|disable|discard|distinctrow|div|dual|dumpfile|each|elseif|enable|enclosed|end|ends|engine|engines|enum|errors|escape|escaped|even|event|events|every|execute|exists|exit|explain|extended|fast|fetch|field|fields|first|flush|for|force|foreign|found_rows|full|fulltext|function|general|global|grant|grants|group|groupby_concat|handler|hash|help|high_priority|hosts|hour_microsecond|hour_minute|hour_second|if|ignore|ignore_server_ids|import|index|index_statistics|infile|inner|innodb|inout|insensitive|insert_method|install|interval|invoker|isolation|iterate|key|keys|kill|language|last|leading|leave|left|level|limit|linear|lines|list|load|local|localtime|localtimestamp|lock|logs|low_priority|master|master_heartbeat_period|master_ssl_verify_server_cert|masters|match|max|max_rows|maxvalue|message_text|middleint|migrate|min|min_rows|minute_microsecond|minute_second|mod|mode|modifies|modify|mutex|mysql_errno|natural|next|no|no_write_to_binlog|offline|offset|one|online|open|optimize|option|optionally|out|outer|outfile|pack_keys|parser|partition|partitions|password|phase|plugin|plugins|prepare|preserve|prev|primary|privileges|procedure|processlist|profile|profiles|purge|query|quick|range|read|read_write|reads|real|rebuild|recover|references|regexp|relaylog|release|remove|rename|reorganize|repair|repeatable|replace|require|resignal|restrict|resume|return|returns|revoke|right|rlike|rollback|rollup|row|row_format|rtree|savepoint|schedule|schema|schema_name|schemas|second_microsecond|security|sensitive|separator|serializable|server|session|share|show|signal|slave|slow|smallint|snapshot|soname|spatial|specific|sql|sql_big_result|sql_buffer_result|sql_cache|sql_calc_found_rows|sql_no_cache|sql_small_result|sqlexception|sqlstate|sqlwarning|ssl|start|starting|starts|status|std|stddev|stddev_pop|stddev_samp|storage|straight_join|subclass_origin|sum|suspend|table_name|table_statistics|tables|tablespace|temporary|terminated|to|trailing|transaction|trigger|triggers|truncate|uncommitted|undo|uninstall|unique|unlock|upgrade|usage|use|use_frm|user|user_resources|user_statistics|using|utc_date|utc_time|utc_timestamp|value|variables|varying|view|views|warnings|when|while|with|work|write|xa|xor|year_month|zerofill|begin|do|then|else|loop|repeat",t="by|bool|boolean|bit|blob|decimal|double|enum|float|long|longblob|longtext|medium|mediumblob|mediumint|mediumtext|time|timestamp|tinyblob|tinyint|tinytext|text|bigint|int|int1|int2|int3|int4|int8|integer|float|float4|float8|double|char|varbinary|varchar|varcharacter|precision|date|datetime|year|unsigned|signed|numeric|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",n="charset|clear|connect|edit|ego|exit|go|help|nopager|notee|nowarning|pager|print|prompt|quit|rehash|source|status|system|tee",r=this.createKeywordMapper({"support.function":t,keyword:e,constant:"false|true|null|unknown|date|time|timestamp|ODBCdotTable|zerolessFloat","variable.language":n},"identifier",!0);this.$rules={start:[{token:"comment",regex:"(?:-- |#).*$"},i({start:'"',escape:/\\[0'"bnrtZ\\%_]?/}),i({start:"'",escape:/\\[0'"bnrtZ\\%_]?/}),s.getStartRule("doc-start"),{token:"comment",regex:/\/\*/,next:"comment"},{token:"constant.numeric",regex:/0[xX][0-9a-fA-F]+|[xX]'[0-9a-fA-F]+'|0[bB][01]+|[bB]'[01]+'/},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"constant.class",regex:"@@?[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"constant.buildin",regex:"`[^`]*`"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}]},this.embedRules(s,"doc-",[s.getEndRule("start")]),this.normalizeRules()};r.inherits(u,o),t.MysqlHighlightRules=u}),define("ace/mode/mysql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/mysql_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../mode/text").Mode,s=e("./mysql_highlight_rules").MysqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart=["--","#"],this.blockComment={start:"/*",end:"*/"},this.$id="ace/mode/mysql"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-objectivec.js b/public/themes/pterodactyl/vendor/ace/mode-objectivec.js deleted file mode 100644 index 98645e5f6..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-objectivec.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/c_cpp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=t.cFunctions="\\b(?:hypot(?:f|l)?|s(?:scanf|ystem|nprintf|ca(?:nf|lb(?:n(?:f|l)?|ln(?:f|l)?))|i(?:n(?:h(?:f|l)?|f|l)?|gn(?:al|bit))|tr(?:s(?:tr|pn)|nc(?:py|at|mp)|c(?:spn|hr|oll|py|at|mp)|to(?:imax|d|u(?:l(?:l)?|max)|k|f|l(?:d|l)?)|error|pbrk|ftime|len|rchr|xfrm)|printf|et(?:jmp|vbuf|locale|buf)|qrt(?:f|l)?|w(?:scanf|printf)|rand)|n(?:e(?:arbyint(?:f|l)?|xt(?:toward(?:f|l)?|after(?:f|l)?))|an(?:f|l)?)|c(?:s(?:in(?:h(?:f|l)?|f|l)?|qrt(?:f|l)?)|cos(?:h(?:f)?|f|l)?|imag(?:f|l)?|t(?:ime|an(?:h(?:f|l)?|f|l)?)|o(?:s(?:h(?:f|l)?|f|l)?|nj(?:f|l)?|pysign(?:f|l)?)|p(?:ow(?:f|l)?|roj(?:f|l)?)|e(?:il(?:f|l)?|xp(?:f|l)?)|l(?:o(?:ck|g(?:f|l)?)|earerr)|a(?:sin(?:h(?:f|l)?|f|l)?|cos(?:h(?:f|l)?|f|l)?|tan(?:h(?:f|l)?|f|l)?|lloc|rg(?:f|l)?|bs(?:f|l)?)|real(?:f|l)?|brt(?:f|l)?)|t(?:ime|o(?:upper|lower)|an(?:h(?:f|l)?|f|l)?|runc(?:f|l)?|gamma(?:f|l)?|mp(?:nam|file))|i(?:s(?:space|n(?:ormal|an)|cntrl|inf|digit|u(?:nordered|pper)|p(?:unct|rint)|finite|w(?:space|c(?:ntrl|type)|digit|upper|p(?:unct|rint)|lower|al(?:num|pha)|graph|xdigit|blank)|l(?:ower|ess(?:equal|greater)?)|al(?:num|pha)|gr(?:eater(?:equal)?|aph)|xdigit|blank)|logb(?:f|l)?|max(?:div|abs))|di(?:v|fftime)|_Exit|unget(?:c|wc)|p(?:ow(?:f|l)?|ut(?:s|c(?:har)?|wc(?:har)?)|error|rintf)|e(?:rf(?:c(?:f|l)?|f|l)?|x(?:it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|v(?:s(?:scanf|nprintf|canf|printf|w(?:scanf|printf))|printf|f(?:scanf|printf|w(?:scanf|printf))|w(?:scanf|printf)|a_(?:start|copy|end|arg))|qsort|f(?:s(?:canf|e(?:tpos|ek))|close|tell|open|dim(?:f|l)?|p(?:classify|ut(?:s|c|w(?:s|c))|rintf)|e(?:holdexcept|set(?:e(?:nv|xceptflag)|round)|clearexcept|testexcept|of|updateenv|r(?:aiseexcept|ror)|get(?:e(?:nv|xceptflag)|round))|flush|w(?:scanf|ide|printf|rite)|loor(?:f|l)?|abs(?:f|l)?|get(?:s|c|pos|w(?:s|c))|re(?:open|e|ad|xp(?:f|l)?)|m(?:in(?:f|l)?|od(?:f|l)?|a(?:f|l|x(?:f|l)?)?))|l(?:d(?:iv|exp(?:f|l)?)|o(?:ngjmp|cal(?:time|econv)|g(?:1(?:p(?:f|l)?|0(?:f|l)?)|2(?:f|l)?|f|l|b(?:f|l)?)?)|abs|l(?:div|abs|r(?:int(?:f|l)?|ound(?:f|l)?))|r(?:int(?:f|l)?|ound(?:f|l)?)|gamma(?:f|l)?)|w(?:scanf|c(?:s(?:s(?:tr|pn)|nc(?:py|at|mp)|c(?:spn|hr|oll|py|at|mp)|to(?:imax|d|u(?:l(?:l)?|max)|k|f|l(?:d|l)?|mbs)|pbrk|ftime|len|r(?:chr|tombs)|xfrm)|to(?:b|mb)|rtomb)|printf|mem(?:set|c(?:hr|py|mp)|move))|a(?:s(?:sert|ctime|in(?:h(?:f|l)?|f|l)?)|cos(?:h(?:f|l)?|f|l)?|t(?:o(?:i|f|l(?:l)?)|exit|an(?:h(?:f|l)?|2(?:f|l)?|f|l)?)|b(?:s|ort))|g(?:et(?:s|c(?:har)?|env|wc(?:har)?)|mtime)|r(?:int(?:f|l)?|ound(?:f|l)?|e(?:name|alloc|wind|m(?:ove|quo(?:f|l)?|ainder(?:f|l)?))|a(?:nd|ise))|b(?:search|towc)|m(?:odf(?:f|l)?|em(?:set|c(?:hr|py|mp)|move)|ktime|alloc|b(?:s(?:init|towcs|rtowcs)|towc|len|r(?:towc|len))))\\b",u=function(){var e="break|case|continue|default|do|else|for|goto|if|_Pragma|return|switch|while|catch|operator|try|throw|using",t="asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void|class|wchar_t|template|char16_t|char32_t",n="const|extern|register|restrict|static|volatile|inline|private|protected|public|friend|explicit|virtual|export|mutable|typename|constexpr|new|delete|alignas|alignof|decltype|noexcept|thread_local",r="and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eqconst_cast|dynamic_cast|reinterpret_cast|static_cast|sizeof|namespace",s="NULL|true|false|TRUE|FALSE|nullptr",u=this.$keywords=this.createKeywordMapper({"keyword.control":e,"storage.type":t,"storage.modifier":n,"keyword.operator":r,"variable.language":"this","constant.language":s},"identifier"),a="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b",f=/\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}U[a-fA-F\d]{8}|.)/.source;this.$rules={start:[{token:"comment",regex:"//$",next:"start"},{token:"comment",regex:"//",next:"singleLineComment"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:"'(?:"+f+"|.)?'"},{token:"string.start",regex:'"',stateName:"qqstring",next:[{token:"string",regex:/\\\s*$/,next:"qqstring"},{token:"constant.language.escape",regex:f},{token:"constant.language.escape",regex:/%[^'"\\]/},{token:"string.end",regex:'"|$',next:"start"},{defaultToken:"string"}]},{token:"string.start",regex:'R"\\(',stateName:"rawString",next:[{token:"string.end",regex:'\\)"',next:"start"},{defaultToken:"string"}]},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"keyword",regex:"#\\s*(?:include|import|pragma|line|define|undef)\\b",next:"directive"},{token:"keyword",regex:"#\\s*(?:endif|if|ifdef|else|elif|ifndef)\\b"},{token:"support.function.C99.c",regex:o},{token:u,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*"},{token:"keyword.operator",regex:/--|\+\+|<<=|>>=|>>>=|<>|&&|\|\||\?:|[*%\/+\-&\^|~!<>=]=?/},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],singleLineComment:[{token:"comment",regex:/\\$/,next:"singleLineComment"},{token:"comment",regex:/$/,next:"start"},{defaultToken:"comment"}],directive:[{token:"constant.other.multiline",regex:/\\/},{token:"constant.other.multiline",regex:/.*\\/},{token:"constant.other",regex:"\\s*<.+?>",next:"start"},{token:"constant.other",regex:'\\s*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]',next:"start"},{token:"constant.other",regex:"\\s*['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']",next:"start"},{token:"constant.other",regex:/[^\\\/]+/,next:"start"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(u,s),t.c_cppHighlightRules=u}),define("ace/mode/objectivec_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/c_cpp_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./c_cpp_highlight_rules"),o=s.c_cppHighlightRules,u=function(){var e="\\\\(?:[abefnrtv'\"?\\\\]|[0-3]\\d{1,2}|[4-7]\\d?|222|x[a-zA-Z0-9]+)",t=[{regex:"\\b_cmd\\b",token:"variable.other.selector.objc"},{regex:"\\b(?:self|super)\\b",token:"variable.language.objc"}],n=new o,r=n.getRules();this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:["storage.type.objc","punctuation.definition.storage.type.objc","entity.name.type.objc","text","entity.other.inherited-class.objc"],regex:"(@)(interface|protocol)(?!.+;)(\\s+[A-Za-z_][A-Za-z0-9_]*)(\\s*:\\s*)([A-Za-z]+)"},{token:["storage.type.objc"],regex:"(@end)"},{token:["storage.type.objc","entity.name.type.objc","entity.other.inherited-class.objc"],regex:"(@implementation)(\\s+[A-Za-z_][A-Za-z0-9_]*)(\\s*?::\\s*(?:[A-Za-z][A-Za-z0-9]*))?"},{token:"string.begin.objc",regex:'@"',next:"constant_NSString"},{token:"storage.type.objc",regex:"\\bid\\s*<",next:"protocol_list"},{token:"keyword.control.macro.objc",regex:"\\bNS_DURING|NS_HANDLER|NS_ENDHANDLER\\b"},{token:["punctuation.definition.keyword.objc","keyword.control.exception.objc"],regex:"(@)(try|catch|finally|throw)\\b"},{token:["punctuation.definition.keyword.objc","keyword.other.objc"],regex:"(@)(defs|encode)\\b"},{token:["storage.type.id.objc","text"],regex:"(\\bid\\b)(\\s|\\n)?"},{token:"storage.type.objc",regex:"\\bIBOutlet|IBAction|BOOL|SEL|id|unichar|IMP|Class\\b"},{token:["punctuation.definition.storage.type.objc","storage.type.objc"],regex:"(@)(class|protocol)\\b"},{token:["punctuation.definition.storage.type.objc","punctuation"],regex:"(@selector)(\\s*\\()",next:"selectors"},{token:["punctuation.definition.storage.modifier.objc","storage.modifier.objc"],regex:"(@)(synchronized|public|private|protected|package)\\b"},{token:"constant.language.objc",regex:"\\bYES|NO|Nil|nil\\b"},{token:"support.variable.foundation",regex:"\\bNSApp\\b"},{token:["support.function.cocoa.leopard"],regex:"(?:\\b)(NS(?:Rect(?:ToCGRect|FromCGRect)|MakeCollectable|S(?:tringFromProtocol|ize(?:ToCGSize|FromCGSize))|Draw(?:NinePartImage|ThreePartImage)|P(?:oint(?:ToCGPoint|FromCGPoint)|rotocolFromString)|EventMaskFromType|Value))(?:\\b)"},{token:["support.function.cocoa"],regex:"(?:\\b)(NS(?:R(?:ound(?:DownToMultipleOfPageSize|UpToMultipleOfPageSize)|un(?:CriticalAlertPanel(?:RelativeToWindow)?|InformationalAlertPanel(?:RelativeToWindow)?|AlertPanel(?:RelativeToWindow)?)|e(?:set(?:MapTable|HashTable)|c(?:ycleZone|t(?:Clip(?:List)?|F(?:ill(?:UsingOperation|List(?:UsingOperation|With(?:Grays|Colors(?:UsingOperation)?))?)?|romString))|ordAllocationEvent)|turnAddress|leaseAlertPanel|a(?:dPixel|l(?:MemoryAvailable|locateCollectable))|gisterServicesProvider)|angeFromString)|Get(?:SizeAndAlignment|CriticalAlertPanel|InformationalAlertPanel|UncaughtExceptionHandler|FileType(?:s)?|WindowServerMemory|AlertPanel)|M(?:i(?:n(?:X|Y)|d(?:X|Y))|ouseInRect|a(?:p(?:Remove|Get|Member|Insert(?:IfAbsent|KnownAbsent)?)|ke(?:R(?:ect|ange)|Size|Point)|x(?:Range|X|Y)))|B(?:itsPer(?:SampleFromDepth|PixelFromDepth)|e(?:stDepth|ep|gin(?:CriticalAlertSheet|InformationalAlertSheet|AlertSheet)))|S(?:ho(?:uldRetainWithZone|w(?:sServicesMenuItem|AnimationEffect))|tringFrom(?:R(?:ect|ange)|MapTable|S(?:ize|elector)|HashTable|Class|Point)|izeFromString|e(?:t(?:ShowsServicesMenuItem|ZoneName|UncaughtExceptionHandler|FocusRingStyle)|lectorFromString|archPathForDirectoriesInDomains)|wap(?:Big(?:ShortToHost|IntToHost|DoubleToHost|FloatToHost|Long(?:ToHost|LongToHost))|Short|Host(?:ShortTo(?:Big|Little)|IntTo(?:Big|Little)|DoubleTo(?:Big|Little)|FloatTo(?:Big|Little)|Long(?:To(?:Big|Little)|LongTo(?:Big|Little)))|Int|Double|Float|L(?:ittle(?:ShortToHost|IntToHost|DoubleToHost|FloatToHost|Long(?:ToHost|LongToHost))|ong(?:Long)?)))|H(?:ighlightRect|o(?:stByteOrder|meDirectory(?:ForUser)?)|eight|ash(?:Remove|Get|Insert(?:IfAbsent|KnownAbsent)?)|FSType(?:CodeFromFileType|OfFile))|N(?:umberOfColorComponents|ext(?:MapEnumeratorPair|HashEnumeratorItem))|C(?:o(?:n(?:tainsRect|vert(?:GlyphsToPackedGlyphs|Swapped(?:DoubleToHost|FloatToHost)|Host(?:DoubleToSwapped|FloatToSwapped)))|unt(?:MapTable|HashTable|Frames|Windows(?:ForContext)?)|py(?:M(?:emoryPages|apTableWithZone)|Bits|HashTableWithZone|Object)|lorSpaceFromDepth|mpare(?:MapTables|HashTables))|lassFromString|reate(?:MapTable(?:WithZone)?|HashTable(?:WithZone)?|Zone|File(?:namePboardType|ContentsPboardType)))|TemporaryDirectory|I(?:s(?:ControllerMarker|EmptyRect|FreedObject)|n(?:setRect|crementExtraRefCount|te(?:r(?:sect(?:sRect|ionR(?:ect|ange))|faceStyleForKey)|gralRect)))|Zone(?:Realloc|Malloc|Name|Calloc|Fr(?:omPointer|ee))|O(?:penStepRootDirectory|ffsetRect)|D(?:i(?:sableScreenUpdates|videRect)|ottedFrameRect|e(?:c(?:imal(?:Round|Multiply|S(?:tring|ubtract)|Normalize|Co(?:py|mpa(?:ct|re))|IsNotANumber|Divide|Power|Add)|rementExtraRefCountWasZero)|faultMallocZone|allocate(?:MemoryPages|Object))|raw(?:Gr(?:oove|ayBezel)|B(?:itmap|utton)|ColorTiledRects|TiledRects|DarkBezel|W(?:hiteBezel|indowBackground)|LightBezel))|U(?:serName|n(?:ionR(?:ect|ange)|registerServicesProvider)|pdateDynamicServices)|Java(?:Bundle(?:Setup|Cleanup)|Setup(?:VirtualMachine)?|Needs(?:ToLoadClasses|VirtualMachine)|ClassesF(?:orBundle|romPath)|ObjectNamedInPath|ProvidesClasses)|P(?:oint(?:InRect|FromString)|erformService|lanarFromDepth|ageSize)|E(?:n(?:d(?:MapTableEnumeration|HashTableEnumeration)|umerate(?:MapTable|HashTable)|ableScreenUpdates)|qual(?:R(?:ects|anges)|Sizes|Points)|raseRect|xtraRefCount)|F(?:ileTypeForHFSTypeCode|ullUserName|r(?:ee(?:MapTable|HashTable)|ame(?:Rect(?:WithWidth(?:UsingOperation)?)?|Address)))|Wi(?:ndowList(?:ForContext)?|dth)|Lo(?:cationInRange|g(?:v|PageSize)?)|A(?:ccessibility(?:R(?:oleDescription(?:ForUIElement)?|aiseBadArgumentException)|Unignored(?:Children(?:ForOnlyChild)?|Descendant|Ancestor)|PostNotification|ActionDescription)|pplication(?:Main|Load)|vailableWindowDepths|ll(?:MapTable(?:Values|Keys)|HashTableObjects|ocate(?:MemoryPages|Collectable|Object)))))(?:\\b)"},{token:["support.class.cocoa.leopard"],regex:"(?:\\b)(NS(?:RuleEditor|G(?:arbageCollector|radient)|MapTable|HashTable|Co(?:ndition|llectionView(?:Item)?)|T(?:oolbarItemGroup|extInputClient|r(?:eeNode|ackingArea))|InvocationOperation|Operation(?:Queue)?|D(?:ictionaryController|ockTile)|P(?:ointer(?:Functions|Array)|athC(?:o(?:ntrol(?:Delegate)?|mponentCell)|ell(?:Delegate)?)|r(?:intPanelAccessorizing|edicateEditor(?:RowTemplate)?))|ViewController|FastEnumeration|Animat(?:ionContext|ablePropertyContainer)))(?:\\b)"},{token:["support.class.cocoa"],regex:"(?:\\b)(NS(?:R(?:u(?:nLoop|ler(?:Marker|View))|e(?:sponder|cursiveLock|lativeSpecifier)|an(?:domSpecifier|geSpecifier))|G(?:etCommand|lyph(?:Generator|Storage|Info)|raphicsContext)|XML(?:Node|D(?:ocument|TD(?:Node)?)|Parser|Element)|M(?:iddleSpecifier|ov(?:ie(?:View)?|eCommand)|utable(?:S(?:tring|et)|C(?:haracterSet|opying)|IndexSet|D(?:ictionary|ata)|URLRequest|ParagraphStyle|A(?:ttributedString|rray))|e(?:ssagePort(?:NameServer)?|nu(?:Item(?:Cell)?|View)?|t(?:hodSignature|adata(?:Item|Query(?:ResultGroup|AttributeValueTuple)?)))|a(?:ch(?:BootstrapServer|Port)|trix))|B(?:itmapImageRep|ox|u(?:ndle|tton(?:Cell)?)|ezierPath|rowser(?:Cell)?)|S(?:hadow|c(?:anner|r(?:ipt(?:SuiteRegistry|C(?:o(?:ercionHandler|mmand(?:Description)?)|lassDescription)|ObjectSpecifier|ExecutionContext|WhoseTest)|oll(?:er|View)|een))|t(?:epper(?:Cell)?|atus(?:Bar|Item)|r(?:ing|eam))|imple(?:HorizontalTypesetter|CString)|o(?:cketPort(?:NameServer)?|und|rtDescriptor)|p(?:e(?:cifierTest|ech(?:Recognizer|Synthesizer)|ll(?:Server|Checker))|litView)|e(?:cureTextField(?:Cell)?|t(?:Command)?|archField(?:Cell)?|rializer|gmentedC(?:ontrol|ell))|lider(?:Cell)?|avePanel)|H(?:ost|TTP(?:Cookie(?:Storage)?|URLResponse)|elpManager)|N(?:ib(?:Con(?:nector|trolConnector)|OutletConnector)?|otification(?:Center|Queue)?|u(?:ll|mber(?:Formatter)?)|etService(?:Browser)?|ameSpecifier)|C(?:ha(?:ngeSpelling|racterSet)|o(?:n(?:stantString|nection|trol(?:ler)?|ditionLock)|d(?:ing|er)|unt(?:Command|edSet)|pying|lor(?:Space|P(?:ick(?:ing(?:Custom|Default)|er)|anel)|Well|List)?|m(?:p(?:oundPredicate|arisonPredicate)|boBox(?:Cell)?))|u(?:stomImageRep|rsor)|IImageRep|ell|l(?:ipView|o(?:seCommand|neCommand)|assDescription)|a(?:ched(?:ImageRep|URLResponse)|lendar(?:Date)?)|reateCommand)|T(?:hread|ypesetter|ime(?:Zone|r)|o(?:olbar(?:Item(?:Validations)?)?|kenField(?:Cell)?)|ext(?:Block|Storage|Container|Tab(?:le(?:Block)?)?|Input|View|Field(?:Cell)?|List|Attachment(?:Cell)?)?|a(?:sk|b(?:le(?:Header(?:Cell|View)|Column|View)|View(?:Item)?))|reeController)|I(?:n(?:dex(?:S(?:pecifier|et)|Path)|put(?:Manager|S(?:tream|erv(?:iceProvider|er(?:MouseTracker)?)))|vocation)|gnoreMisspelledWords|mage(?:Rep|Cell|View)?)|O(?:ut(?:putStream|lineView)|pen(?:GL(?:Context|Pixel(?:Buffer|Format)|View)|Panel)|bj(?:CTypeSerializationCallBack|ect(?:Controller)?))|D(?:i(?:st(?:antObject(?:Request)?|ributed(?:NotificationCenter|Lock))|ctionary|rectoryEnumerator)|ocument(?:Controller)?|e(?:serializer|cimalNumber(?:Behaviors|Handler)?|leteCommand)|at(?:e(?:Components|Picker(?:Cell)?|Formatter)?|a)|ra(?:wer|ggingInfo))|U(?:ser(?:InterfaceValidations|Defaults(?:Controller)?)|RL(?:Re(?:sponse|quest)|Handle(?:Client)?|C(?:onnection|ache|redential(?:Storage)?)|Download(?:Delegate)?|Prot(?:ocol(?:Client)?|ectionSpace)|AuthenticationChallenge(?:Sender)?)?|n(?:iqueIDSpecifier|doManager|archiver))|P(?:ipe|o(?:sitionalSpecifier|pUpButton(?:Cell)?|rt(?:Message|NameServer|Coder)?)|ICTImageRep|ersistentDocument|DFImageRep|a(?:steboard|nel|ragraphStyle|geLayout)|r(?:int(?:Info|er|Operation|Panel)|o(?:cessInfo|tocolChecker|perty(?:Specifier|ListSerialization)|gressIndicator|xy)|edicate))|E(?:numerator|vent|PSImageRep|rror|x(?:ception|istsCommand|pression))|V(?:iew(?:Animation)?|al(?:idated(?:ToobarItem|UserInterfaceItem)|ue(?:Transformer)?))|Keyed(?:Unarchiver|Archiver)|Qui(?:ckDrawView|tCommand)|F(?:ile(?:Manager|Handle|Wrapper)|o(?:nt(?:Manager|Descriptor|Panel)?|rm(?:Cell|atter)))|W(?:hoseSpecifier|indow(?:Controller)?|orkspace)|L(?:o(?:c(?:k(?:ing)?|ale)|gicalTest)|evelIndicator(?:Cell)?|ayoutManager)|A(?:ssertionHandler|nimation|ctionCell|ttributedString|utoreleasePool|TSTypesetter|ppl(?:ication|e(?:Script|Event(?:Manager|Descriptor)))|ffineTransform|lert|r(?:chiver|ray(?:Controller)?))))(?:\\b)"},{token:["support.type.cocoa.leopard"],regex:"(?:\\b)(NS(?:R(?:u(?:nLoop|ler(?:Marker|View))|e(?:sponder|cursiveLock|lativeSpecifier)|an(?:domSpecifier|geSpecifier))|G(?:etCommand|lyph(?:Generator|Storage|Info)|raphicsContext)|XML(?:Node|D(?:ocument|TD(?:Node)?)|Parser|Element)|M(?:iddleSpecifier|ov(?:ie(?:View)?|eCommand)|utable(?:S(?:tring|et)|C(?:haracterSet|opying)|IndexSet|D(?:ictionary|ata)|URLRequest|ParagraphStyle|A(?:ttributedString|rray))|e(?:ssagePort(?:NameServer)?|nu(?:Item(?:Cell)?|View)?|t(?:hodSignature|adata(?:Item|Query(?:ResultGroup|AttributeValueTuple)?)))|a(?:ch(?:BootstrapServer|Port)|trix))|B(?:itmapImageRep|ox|u(?:ndle|tton(?:Cell)?)|ezierPath|rowser(?:Cell)?)|S(?:hadow|c(?:anner|r(?:ipt(?:SuiteRegistry|C(?:o(?:ercionHandler|mmand(?:Description)?)|lassDescription)|ObjectSpecifier|ExecutionContext|WhoseTest)|oll(?:er|View)|een))|t(?:epper(?:Cell)?|atus(?:Bar|Item)|r(?:ing|eam))|imple(?:HorizontalTypesetter|CString)|o(?:cketPort(?:NameServer)?|und|rtDescriptor)|p(?:e(?:cifierTest|ech(?:Recognizer|Synthesizer)|ll(?:Server|Checker))|litView)|e(?:cureTextField(?:Cell)?|t(?:Command)?|archField(?:Cell)?|rializer|gmentedC(?:ontrol|ell))|lider(?:Cell)?|avePanel)|H(?:ost|TTP(?:Cookie(?:Storage)?|URLResponse)|elpManager)|N(?:ib(?:Con(?:nector|trolConnector)|OutletConnector)?|otification(?:Center|Queue)?|u(?:ll|mber(?:Formatter)?)|etService(?:Browser)?|ameSpecifier)|C(?:ha(?:ngeSpelling|racterSet)|o(?:n(?:stantString|nection|trol(?:ler)?|ditionLock)|d(?:ing|er)|unt(?:Command|edSet)|pying|lor(?:Space|P(?:ick(?:ing(?:Custom|Default)|er)|anel)|Well|List)?|m(?:p(?:oundPredicate|arisonPredicate)|boBox(?:Cell)?))|u(?:stomImageRep|rsor)|IImageRep|ell|l(?:ipView|o(?:seCommand|neCommand)|assDescription)|a(?:ched(?:ImageRep|URLResponse)|lendar(?:Date)?)|reateCommand)|T(?:hread|ypesetter|ime(?:Zone|r)|o(?:olbar(?:Item(?:Validations)?)?|kenField(?:Cell)?)|ext(?:Block|Storage|Container|Tab(?:le(?:Block)?)?|Input|View|Field(?:Cell)?|List|Attachment(?:Cell)?)?|a(?:sk|b(?:le(?:Header(?:Cell|View)|Column|View)|View(?:Item)?))|reeController)|I(?:n(?:dex(?:S(?:pecifier|et)|Path)|put(?:Manager|S(?:tream|erv(?:iceProvider|er(?:MouseTracker)?)))|vocation)|gnoreMisspelledWords|mage(?:Rep|Cell|View)?)|O(?:ut(?:putStream|lineView)|pen(?:GL(?:Context|Pixel(?:Buffer|Format)|View)|Panel)|bj(?:CTypeSerializationCallBack|ect(?:Controller)?))|D(?:i(?:st(?:antObject(?:Request)?|ributed(?:NotificationCenter|Lock))|ctionary|rectoryEnumerator)|ocument(?:Controller)?|e(?:serializer|cimalNumber(?:Behaviors|Handler)?|leteCommand)|at(?:e(?:Components|Picker(?:Cell)?|Formatter)?|a)|ra(?:wer|ggingInfo))|U(?:ser(?:InterfaceValidations|Defaults(?:Controller)?)|RL(?:Re(?:sponse|quest)|Handle(?:Client)?|C(?:onnection|ache|redential(?:Storage)?)|Download(?:Delegate)?|Prot(?:ocol(?:Client)?|ectionSpace)|AuthenticationChallenge(?:Sender)?)?|n(?:iqueIDSpecifier|doManager|archiver))|P(?:ipe|o(?:sitionalSpecifier|pUpButton(?:Cell)?|rt(?:Message|NameServer|Coder)?)|ICTImageRep|ersistentDocument|DFImageRep|a(?:steboard|nel|ragraphStyle|geLayout)|r(?:int(?:Info|er|Operation|Panel)|o(?:cessInfo|tocolChecker|perty(?:Specifier|ListSerialization)|gressIndicator|xy)|edicate))|E(?:numerator|vent|PSImageRep|rror|x(?:ception|istsCommand|pression))|V(?:iew(?:Animation)?|al(?:idated(?:ToobarItem|UserInterfaceItem)|ue(?:Transformer)?))|Keyed(?:Unarchiver|Archiver)|Qui(?:ckDrawView|tCommand)|F(?:ile(?:Manager|Handle|Wrapper)|o(?:nt(?:Manager|Descriptor|Panel)?|rm(?:Cell|atter)))|W(?:hoseSpecifier|indow(?:Controller)?|orkspace)|L(?:o(?:c(?:k(?:ing)?|ale)|gicalTest)|evelIndicator(?:Cell)?|ayoutManager)|A(?:ssertionHandler|nimation|ctionCell|ttributedString|utoreleasePool|TSTypesetter|ppl(?:ication|e(?:Script|Event(?:Manager|Descriptor)))|ffineTransform|lert|r(?:chiver|ray(?:Controller)?))))(?:\\b)"},{token:["support.class.quartz"],regex:"(?:\\b)(C(?:I(?:Sampler|Co(?:ntext|lor)|Image(?:Accumulator)?|PlugIn(?:Registration)?|Vector|Kernel|Filter(?:Generator|Shape)?)|A(?:Renderer|MediaTiming(?:Function)?|BasicAnimation|ScrollLayer|Constraint(?:LayoutManager)?|T(?:iledLayer|extLayer|rans(?:ition|action))|OpenGLLayer|PropertyAnimation|KeyframeAnimation|Layer|A(?:nimation(?:Group)?|ction))))(?:\\b)"},{token:["support.type.quartz"],regex:"(?:\\b)(C(?:G(?:Float|Point|Size|Rect)|IFormat|AConstraintAttribute))(?:\\b)"},{token:["support.type.cocoa"],regex:"(?:\\b)(NS(?:R(?:ect(?:Edge)?|ange)|G(?:lyph(?:Relation|LayoutMode)?|radientType)|M(?:odalSession|a(?:trixMode|p(?:Table|Enumerator)))|B(?:itmapImageFileType|orderType|uttonType|ezelStyle|ackingStoreType|rowserColumnResizingType)|S(?:cr(?:oll(?:er(?:Part|Arrow)|ArrowPosition)|eenAuxiliaryOpaque)|tringEncoding|ize|ocketNativeHandle|election(?:Granularity|Direction|Affinity)|wapped(?:Double|Float)|aveOperationType)|Ha(?:sh(?:Table|Enumerator)|ndler(?:2)?)|C(?:o(?:ntrol(?:Size|Tint)|mp(?:ositingOperation|arisonResult))|ell(?:State|Type|ImagePosition|Attribute))|T(?:hreadPrivate|ypesetterGlyphInfo|i(?:ckMarkPosition|tlePosition|meInterval)|o(?:ol(?:TipTag|bar(?:SizeMode|DisplayMode))|kenStyle)|IFFCompression|ext(?:TabType|Alignment)|ab(?:State|leViewDropOperation|ViewType)|rackingRectTag)|ImageInterpolation|Zone|OpenGL(?:ContextAuxiliary|PixelFormatAuxiliary)|D(?:ocumentChangeType|atePickerElementFlags|ra(?:werState|gOperation))|UsableScrollerParts|P(?:oint|r(?:intingPageOrder|ogressIndicator(?:Style|Th(?:ickness|readInfo))))|EventType|KeyValueObservingOptions|Fo(?:nt(?:SymbolicTraits|TraitMask|Action)|cusRingType)|W(?:indow(?:OrderingMode|Depth)|orkspace(?:IconCreationOptions|LaunchOptions)|ritingDirection)|L(?:ineBreakMode|ayout(?:Status|Direction))|A(?:nimation(?:Progress|Effect)|ppl(?:ication(?:TerminateReply|DelegateReply|PrintReply)|eEventManagerSuspensionID)|ffineTransformStruct|lertStyle)))(?:\\b)"},{token:["support.constant.cocoa"],regex:"(?:\\b)(NS(?:NotFound|Ordered(?:Ascending|Descending|Same)))(?:\\b)"},{token:["support.constant.notification.cocoa.leopard"],regex:"(?:\\b)(NS(?:MenuDidBeginTracking|ViewDidUpdateTrackingAreas)?Notification)(?:\\b)"},{token:["support.constant.notification.cocoa"],regex:"(?:\\b)(NS(?:Menu(?:Did(?:RemoveItem|SendAction|ChangeItem|EndTracking|AddItem)|WillSendAction)|S(?:ystemColorsDidChange|plitView(?:DidResizeSubviews|WillResizeSubviews))|C(?:o(?:nt(?:extHelpModeDid(?:Deactivate|Activate)|rolT(?:intDidChange|extDid(?:BeginEditing|Change|EndEditing)))|lor(?:PanelColorDidChange|ListDidChange)|mboBox(?:Selection(?:IsChanging|DidChange)|Will(?:Dismiss|PopUp)))|lassDescriptionNeededForClass)|T(?:oolbar(?:DidRemoveItem|WillAddItem)|ext(?:Storage(?:DidProcessEditing|WillProcessEditing)|Did(?:BeginEditing|Change|EndEditing)|View(?:DidChange(?:Selection|TypingAttributes)|WillChangeNotifyingTextView))|ableView(?:Selection(?:IsChanging|DidChange)|ColumnDid(?:Resize|Move)))|ImageRepRegistryDidChange|OutlineView(?:Selection(?:IsChanging|DidChange)|ColumnDid(?:Resize|Move)|Item(?:Did(?:Collapse|Expand)|Will(?:Collapse|Expand)))|Drawer(?:Did(?:Close|Open)|Will(?:Close|Open))|PopUpButton(?:CellWillPopUp|WillPopUp)|View(?:GlobalFrameDidChange|BoundsDidChange|F(?:ocusDidChange|rameDidChange))|FontSetChanged|W(?:indow(?:Did(?:Resi(?:ze|gn(?:Main|Key))|M(?:iniaturize|ove)|Become(?:Main|Key)|ChangeScreen(?:|Profile)|Deminiaturize|Update|E(?:ndSheet|xpose))|Will(?:M(?:iniaturize|ove)|BeginSheet|Close))|orkspace(?:SessionDid(?:ResignActive|BecomeActive)|Did(?:Mount|TerminateApplication|Unmount|PerformFileOperation|Wake|LaunchApplication)|Will(?:Sleep|Unmount|PowerOff|LaunchApplication)))|A(?:ntialiasThresholdChanged|ppl(?:ication(?:Did(?:ResignActive|BecomeActive|Hide|ChangeScreenParameters|U(?:nhide|pdate)|FinishLaunching)|Will(?:ResignActive|BecomeActive|Hide|Terminate|U(?:nhide|pdate)|FinishLaunching))|eEventManagerWillProcessFirstEvent)))Notification)(?:\\b)"},{token:["support.constant.cocoa.leopard"],regex:"(?:\\b)(NS(?:RuleEditor(?:RowType(?:Simple|Compound)|NestingMode(?:Si(?:ngle|mple)|Compound|List))|GradientDraws(?:BeforeStartingLocation|AfterEndingLocation)|M(?:inusSetExpressionType|a(?:chPortDeallocate(?:ReceiveRight|SendRight|None)|pTable(?:StrongMemory|CopyIn|ZeroingWeakMemory|ObjectPointerPersonality)))|B(?:oxCustom|undleExecutableArchitecture(?:X86|I386|PPC(?:64)?)|etweenPredicateOperatorType|ackgroundStyle(?:Raised|Dark|L(?:ight|owered)))|S(?:tring(?:DrawingTruncatesLastVisibleLine|EncodingConversion(?:ExternalRepresentation|AllowLossy))|ubqueryExpressionType|p(?:e(?:ech(?:SentenceBoundary|ImmediateBoundary|WordBoundary)|llingState(?:GrammarFlag|SpellingFlag))|litViewDividerStyleThi(?:n|ck))|e(?:rvice(?:RequestTimedOutError|M(?:iscellaneousError|alformedServiceDictionaryError)|InvalidPasteboardDataError|ErrorM(?:inimum|aximum)|Application(?:NotFoundError|LaunchFailedError))|gmentStyle(?:Round(?:Rect|ed)|SmallSquare|Capsule|Textured(?:Rounded|Square)|Automatic)))|H(?:UDWindowMask|ashTable(?:StrongMemory|CopyIn|ZeroingWeakMemory|ObjectPointerPersonality))|N(?:oModeColorPanel|etServiceNoAutoRename)|C(?:hangeRedone|o(?:ntainsPredicateOperatorType|l(?:orRenderingIntent(?:RelativeColorimetric|Saturation|Default|Perceptual|AbsoluteColorimetric)|lectorDisabledOption))|ellHit(?:None|ContentArea|TrackableArea|EditableTextArea))|T(?:imeZoneNameStyle(?:S(?:hort(?:Standard|DaylightSaving)|tandard)|DaylightSaving)|extFieldDatePickerStyle|ableViewSelectionHighlightStyle(?:Regular|SourceList)|racking(?:Mouse(?:Moved|EnteredAndExited)|CursorUpdate|InVisibleRect|EnabledDuringMouseDrag|A(?:ssumeInside|ctive(?:In(?:KeyWindow|ActiveApp)|WhenFirstResponder|Always))))|I(?:n(?:tersectSetExpressionType|dexedColorSpaceModel)|mageScale(?:None|Proportionally(?:Down|UpOrDown)|AxesIndependently))|Ope(?:nGLPFAAllowOfflineRenderers|rationQueue(?:DefaultMaxConcurrentOperationCount|Priority(?:High|Normal|Very(?:High|Low)|Low)))|D(?:iacriticInsensitiveSearch|ownloadsDirectory)|U(?:nionSetExpressionType|TF(?:16(?:BigEndianStringEncoding|StringEncoding|LittleEndianStringEncoding)|32(?:BigEndianStringEncoding|StringEncoding|LittleEndianStringEncoding)))|P(?:ointerFunctions(?:Ma(?:chVirtualMemory|llocMemory)|Str(?:ongMemory|uctPersonality)|C(?:StringPersonality|opyIn)|IntegerPersonality|ZeroingWeakMemory|O(?:paque(?:Memory|Personality)|bjectP(?:ointerPersonality|ersonality)))|at(?:hStyle(?:Standard|NavigationBar|PopUp)|ternColorSpaceModel)|rintPanelShows(?:Scaling|Copies|Orientation|P(?:a(?:perSize|ge(?:Range|SetupAccessory))|review)))|Executable(?:RuntimeMismatchError|NotLoadableError|ErrorM(?:inimum|aximum)|L(?:inkError|oadError)|ArchitectureMismatchError)|KeyValueObservingOption(?:Initial|Prior)|F(?:i(?:ndPanelSubstringMatchType(?:StartsWith|Contains|EndsWith|FullWord)|leRead(?:TooLargeError|UnknownStringEncodingError))|orcedOrderingSearch)|Wi(?:ndow(?:BackingLocation(?:MainMemory|Default|VideoMemory)|Sharing(?:Read(?:Only|Write)|None)|CollectionBehavior(?:MoveToActiveSpace|CanJoinAllSpaces|Default))|dthInsensitiveSearch)|AggregateExpressionType))(?:\\b)"},{token:["support.constant.cocoa"],regex:"(?:\\b)(NS(?:R(?:GB(?:ModeColorPanel|ColorSpaceModel)|ight(?:Mouse(?:D(?:own(?:Mask)?|ragged(?:Mask)?)|Up(?:Mask)?)|T(?:ext(?:Movement|Alignment)|ab(?:sBezelBorder|StopType))|ArrowFunctionKey)|ound(?:RectBezelStyle|Bankers|ed(?:BezelStyle|TokenStyle|DisclosureBezelStyle)|Down|Up|Plain|Line(?:CapStyle|JoinStyle))|un(?:StoppedResponse|ContinuesResponse|AbortedResponse)|e(?:s(?:izableWindowMask|et(?:CursorRectsRunLoopOrdering|FunctionKey))|ce(?:ssedBezelStyle|iver(?:sCantHandleCommandScriptError|EvaluationScriptError))|turnTextMovement|doFunctionKey|quiredArgumentsMissingScriptError|l(?:evancyLevelIndicatorStyle|ative(?:Before|After))|gular(?:SquareBezelStyle|ControlSize)|moveTraitFontAction)|a(?:n(?:domSubelement|geDateMode)|tingLevelIndicatorStyle|dio(?:ModeMatrix|Button)))|G(?:IFFileType|lyph(?:Below|Inscribe(?:B(?:elow|ase)|Over(?:strike|Below)|Above)|Layout(?:WithPrevious|A(?:tAPoint|gainstAPoint))|A(?:ttribute(?:BidiLevel|Soft|Inscribe|Elastic)|bove))|r(?:ooveBorder|eaterThan(?:Comparison|OrEqualTo(?:Comparison|PredicateOperatorType)|PredicateOperatorType)|a(?:y(?:ModeColorPanel|ColorSpaceModel)|dient(?:None|Con(?:cave(?:Strong|Weak)|vex(?:Strong|Weak)))|phiteControlTint)))|XML(?:N(?:o(?:tationDeclarationKind|de(?:CompactEmptyElement|IsCDATA|OptionsNone|Use(?:SingleQuotes|DoubleQuotes)|Pre(?:serve(?:NamespaceOrder|C(?:haracterReferences|DATA)|DTD|Prefixes|E(?:ntities|mptyElements)|Quotes|Whitespace|A(?:ttributeOrder|ll))|ttyPrint)|ExpandEmptyElement))|amespaceKind)|CommentKind|TextKind|InvalidKind|D(?:ocument(?:X(?:MLKind|HTMLKind|Include)|HTMLKind|T(?:idy(?:XML|HTML)|extKind)|IncludeContentTypeDeclaration|Validate|Kind)|TDKind)|P(?:arser(?:GTRequiredError|XMLDeclNot(?:StartedError|FinishedError)|Mi(?:splaced(?:XMLDeclarationError|CDATAEndStringError)|xedContentDeclNot(?:StartedError|FinishedError))|S(?:t(?:andaloneValueError|ringNot(?:StartedError|ClosedError))|paceRequiredError|eparatorRequiredError)|N(?:MTOKENRequiredError|o(?:t(?:ationNot(?:StartedError|FinishedError)|WellBalancedError)|DTDError)|amespaceDeclarationError|AMERequiredError)|C(?:haracterRef(?:In(?:DTDError|PrologError|EpilogError)|AtEOFError)|o(?:nditionalSectionNot(?:StartedError|FinishedError)|mment(?:NotFinishedError|ContainsDoubleHyphenError))|DATANotFinishedError)|TagNameMismatchError|In(?:ternalError|valid(?:HexCharacterRefError|C(?:haracter(?:RefError|InEntityError|Error)|onditionalSectionError)|DecimalCharacterRefError|URIError|Encoding(?:NameError|Error)))|OutOfMemoryError|D(?:ocumentStartError|elegateAbortedParseError|OCTYPEDeclNotFinishedError)|U(?:RI(?:RequiredError|FragmentError)|n(?:declaredEntityError|parsedEntityError|knownEncodingError|finishedTagError))|P(?:CDATARequiredError|ublicIdentifierRequiredError|arsedEntityRef(?:MissingSemiError|NoNameError|In(?:Internal(?:SubsetError|Error)|PrologError|EpilogError)|AtEOFError)|r(?:ocessingInstructionNot(?:StartedError|FinishedError)|ematureDocumentEndError))|E(?:n(?:codingNotSupportedError|tity(?:Ref(?:In(?:DTDError|PrologError|EpilogError)|erence(?:MissingSemiError|WithoutNameError)|LoopError|AtEOFError)|BoundaryError|Not(?:StartedError|FinishedError)|Is(?:ParameterError|ExternalError)|ValueRequiredError))|qualExpectedError|lementContentDeclNot(?:StartedError|FinishedError)|xt(?:ernalS(?:tandaloneEntityError|ubsetNotFinishedError)|raContentError)|mptyDocumentError)|L(?:iteralNot(?:StartedError|FinishedError)|T(?:RequiredError|SlashRequiredError)|essThanSymbolInAttributeError)|Attribute(?:RedefinedError|HasNoValueError|Not(?:StartedError|FinishedError)|ListNot(?:StartedError|FinishedError)))|rocessingInstructionKind)|E(?:ntity(?:GeneralKind|DeclarationKind|UnparsedKind|P(?:ar(?:sedKind|ameterKind)|redefined))|lement(?:Declaration(?:MixedKind|UndefinedKind|E(?:lementKind|mptyKind)|Kind|AnyKind)|Kind))|Attribute(?:N(?:MToken(?:sKind|Kind)|otationKind)|CDATAKind|ID(?:Ref(?:sKind|Kind)|Kind)|DeclarationKind|En(?:tit(?:yKind|iesKind)|umerationKind)|Kind))|M(?:i(?:n(?:XEdge|iaturizableWindowMask|YEdge|uteCalendarUnit)|terLineJoinStyle|ddleSubelement|xedState)|o(?:nthCalendarUnit|deSwitchFunctionKey|use(?:Moved(?:Mask)?|E(?:ntered(?:Mask)?|ventSubtype|xited(?:Mask)?))|veToBezierPathElement|mentary(?:ChangeButton|Push(?:Button|InButton)|Light(?:Button)?))|enuFunctionKey|a(?:c(?:intoshInterfaceStyle|OSRomanStringEncoding)|tchesPredicateOperatorType|ppedRead|x(?:XEdge|YEdge))|ACHOperatingSystem)|B(?:MPFileType|o(?:ttomTabsBezelBorder|ldFontMask|rderlessWindowMask|x(?:Se(?:condary|parator)|OldStyle|Primary))|uttLineCapStyle|e(?:zelBorder|velLineJoinStyle|low(?:Bottom|Top)|gin(?:sWith(?:Comparison|PredicateOperatorType)|FunctionKey))|lueControlTint|ack(?:spaceCharacter|tabTextMovement|ingStore(?:Retained|Buffered|Nonretained)|TabCharacter|wardsSearch|groundTab)|r(?:owser(?:NoColumnResizing|UserColumnResizing|AutoColumnResizing)|eakFunctionKey))|S(?:h(?:ift(?:JISStringEncoding|KeyMask)|ow(?:ControlGlyphs|InvisibleGlyphs)|adowlessSquareBezelStyle)|y(?:s(?:ReqFunctionKey|tem(?:D(?:omainMask|efined(?:Mask)?)|FunctionKey))|mbolStringEncoding)|c(?:a(?:nnedOption|le(?:None|ToFit|Proportionally))|r(?:oll(?:er(?:NoPart|Increment(?:Page|Line|Arrow)|Decrement(?:Page|Line|Arrow)|Knob(?:Slot)?|Arrows(?:M(?:inEnd|axEnd)|None|DefaultSetting))|Wheel(?:Mask)?|LockFunctionKey)|eenChangedEventType))|t(?:opFunctionKey|r(?:ingDrawing(?:OneShot|DisableScreenFontSubstitution|Uses(?:DeviceMetrics|FontLeading|LineFragmentOrigin))|eam(?:Status(?:Reading|NotOpen|Closed|Open(?:ing)?|Error|Writing|AtEnd)|Event(?:Has(?:BytesAvailable|SpaceAvailable)|None|OpenCompleted|E(?:ndEncountered|rrorOccurred)))))|i(?:ngle(?:DateMode|UnderlineStyle)|ze(?:DownFontAction|UpFontAction))|olarisOperatingSystem|unOSOperatingSystem|pecialPageOrder|e(?:condCalendarUnit|lect(?:By(?:Character|Paragraph|Word)|i(?:ng(?:Next|Previous)|onAffinity(?:Downstream|Upstream))|edTab|FunctionKey)|gmentSwitchTracking(?:Momentary|Select(?:One|Any)))|quareLineCapStyle|witchButton|ave(?:ToOperation|Op(?:tions(?:Yes|No|Ask)|eration)|AsOperation)|mall(?:SquareBezelStyle|C(?:ontrolSize|apsFontMask)|IconButtonBezelStyle))|H(?:ighlightModeMatrix|SBModeColorPanel|o(?:ur(?:Minute(?:SecondDatePickerElementFlag|DatePickerElementFlag)|CalendarUnit)|rizontalRuler|meFunctionKey)|TTPCookieAcceptPolicy(?:Never|OnlyFromMainDocumentDomain|Always)|e(?:lp(?:ButtonBezelStyle|KeyMask|FunctionKey)|avierFontAction)|PUXOperatingSystem)|Year(?:MonthDa(?:yDatePickerElementFlag|tePickerElementFlag)|CalendarUnit)|N(?:o(?:n(?:StandardCharacterSetFontMask|ZeroWindingRule|activatingPanelMask|LossyASCIIStringEncoding)|Border|t(?:ification(?:SuspensionBehavior(?:Hold|Coalesce|D(?:eliverImmediately|rop))|NoCoalescing|CoalescingOn(?:Sender|Name)|DeliverImmediately|PostToAllSessions)|PredicateType|EqualToPredicateOperatorType)|S(?:cr(?:iptError|ollerParts)|ubelement|pecifierError)|CellMask|T(?:itle|opLevelContainersSpecifierError|abs(?:BezelBorder|NoBorder|LineBorder))|I(?:nterfaceStyle|mage)|UnderlineStyle|FontChangeAction)|u(?:ll(?:Glyph|CellType)|m(?:eric(?:Search|PadKeyMask)|berFormatter(?:Round(?:Half(?:Down|Up|Even)|Ceiling|Down|Up|Floor)|Behavior(?:10|Default)|S(?:cientificStyle|pellOutStyle)|NoStyle|CurrencyStyle|DecimalStyle|P(?:ercentStyle|ad(?:Before(?:Suffix|Prefix)|After(?:Suffix|Prefix))))))|e(?:t(?:Service(?:BadArgumentError|NotFoundError|C(?:ollisionError|ancelledError)|TimeoutError|InvalidError|UnknownError|ActivityInProgress)|workDomainMask)|wlineCharacter|xt(?:StepInterfaceStyle|FunctionKey))|EXTSTEPStringEncoding|a(?:t(?:iveShortGlyphPacking|uralTextAlignment)|rrowFontMask))|C(?:hange(?:ReadOtherContents|GrayCell(?:Mask)?|BackgroundCell(?:Mask)?|Cleared|Done|Undone|Autosaved)|MYK(?:ModeColorPanel|ColorSpaceModel)|ircular(?:BezelStyle|Slider)|o(?:n(?:stantValueExpressionType|t(?:inuousCapacityLevelIndicatorStyle|entsCellMask|ain(?:sComparison|erSpecifierError)|rol(?:Glyph|KeyMask))|densedFontMask)|lor(?:Panel(?:RGBModeMask|GrayModeMask|HSBModeMask|C(?:MYKModeMask|olorListModeMask|ustomPaletteModeMask|rayonModeMask)|WheelModeMask|AllModesMask)|ListModeColorPanel)|reServiceDirectory|m(?:p(?:osite(?:XOR|Source(?:In|O(?:ut|ver)|Atop)|Highlight|C(?:opy|lear)|Destination(?:In|O(?:ut|ver)|Atop)|Plus(?:Darker|Lighter))|ressedFontMask)|mandKeyMask))|u(?:stom(?:SelectorPredicateOperatorType|PaletteModeColorPanel)|r(?:sor(?:Update(?:Mask)?|PointingDevice)|veToBezierPathElement))|e(?:nterT(?:extAlignment|abStopType)|ll(?:State|H(?:ighlighted|as(?:Image(?:Horizontal|OnLeftOrBottom)|OverlappingImage))|ChangesContents|Is(?:Bordered|InsetButton)|Disabled|Editable|LightsBy(?:Gray|Background|Contents)|AllowsMixedState))|l(?:ipPagination|o(?:s(?:ePathBezierPathElement|ableWindowMask)|ckAndCalendarDatePickerStyle)|ear(?:ControlTint|DisplayFunctionKey|LineFunctionKey))|a(?:seInsensitive(?:Search|PredicateOption)|n(?:notCreateScriptCommandError|cel(?:Button|TextMovement))|chesDirectory|lculation(?:NoError|Overflow|DivideByZero|Underflow|LossOfPrecision)|rriageReturnCharacter)|r(?:itical(?:Request|AlertStyle)|ayonModeColorPanel))|T(?:hick(?:SquareBezelStyle|erSquareBezelStyle)|ypesetter(?:Behavior|HorizontalTabAction|ContainerBreakAction|ZeroAdvancementAction|OriginalBehavior|ParagraphBreakAction|WhitespaceAction|L(?:ineBreakAction|atestBehavior))|i(?:ckMark(?:Right|Below|Left|Above)|tledWindowMask|meZoneDatePickerElementFlag)|o(?:olbarItemVisibilityPriority(?:Standard|High|User|Low)|pTabsBezelBorder|ggleButton)|IFF(?:Compression(?:N(?:one|EXT)|CCITTFAX(?:3|4)|OldJPEG|JPEG|PackBits|LZW)|FileType)|e(?:rminate(?:Now|Cancel|Later)|xt(?:Read(?:InapplicableDocumentTypeError|WriteErrorM(?:inimum|aximum))|Block(?:M(?:i(?:nimum(?:Height|Width)|ddleAlignment)|a(?:rgin|ximum(?:Height|Width)))|B(?:o(?:ttomAlignment|rder)|aselineAlignment)|Height|TopAlignment|P(?:ercentageValueType|adding)|Width|AbsoluteValueType)|StorageEdited(?:Characters|Attributes)|CellType|ured(?:RoundedBezelStyle|BackgroundWindowMask|SquareBezelStyle)|Table(?:FixedLayoutAlgorithm|AutomaticLayoutAlgorithm)|Field(?:RoundedBezel|SquareBezel|AndStepperDatePickerStyle)|WriteInapplicableDocumentTypeError|ListPrependEnclosingMarker))|woByteGlyphPacking|ab(?:Character|TextMovement|le(?:tP(?:oint(?:Mask|EventSubtype)?|roximity(?:Mask|EventSubtype)?)|Column(?:NoResizing|UserResizingMask|AutoresizingMask)|View(?:ReverseSequentialColumnAutoresizingStyle|GridNone|S(?:olid(?:HorizontalGridLineMask|VerticalGridLineMask)|equentialColumnAutoresizingStyle)|NoColumnAutoresizing|UniformColumnAutoresizingStyle|FirstColumnOnlyAutoresizingStyle|LastColumnOnlyAutoresizingStyle)))|rackModeMatrix)|I(?:n(?:sert(?:CharFunctionKey|FunctionKey|LineFunctionKey)|t(?:Type|ernalS(?:criptError|pecifierError))|dexSubelement|validIndexSpecifierError|formational(?:Request|AlertStyle)|PredicateOperatorType)|talicFontMask|SO(?:2022JPStringEncoding|Latin(?:1StringEncoding|2StringEncoding))|dentityMappingCharacterCollection|llegalTextMovement|mage(?:R(?:ight|ep(?:MatchesDevice|LoadStatus(?:ReadingHeader|Completed|InvalidData|Un(?:expectedEOF|knownType)|WillNeedAllData)))|Below|C(?:ellType|ache(?:BySize|Never|Default|Always))|Interpolation(?:High|None|Default|Low)|O(?:nly|verlaps)|Frame(?:Gr(?:oove|ayBezel)|Button|None|Photo)|L(?:oadStatus(?:ReadError|C(?:ompleted|ancelled)|InvalidData|UnexpectedEOF)|eft)|A(?:lign(?:Right|Bottom(?:Right|Left)?|Center|Top(?:Right|Left)?|Left)|bove)))|O(?:n(?:State|eByteGlyphPacking|OffButton|lyScrollerArrows)|ther(?:Mouse(?:D(?:own(?:Mask)?|ragged(?:Mask)?)|Up(?:Mask)?)|TextMovement)|SF1OperatingSystem|pe(?:n(?:GL(?:GO(?:Re(?:setLibrary|tainRenderers)|ClearFormatCache|FormatCacheSize)|PFA(?:R(?:obust|endererID)|M(?:inimumPolicy|ulti(?:sample|Screen)|PSafe|aximumPolicy)|BackingStore|S(?:creenMask|te(?:ncilSize|reo)|ingleRenderer|upersample|ample(?:s|Buffers|Alpha))|NoRecovery|C(?:o(?:lor(?:Size|Float)|mpliant)|losestPolicy)|OffScreen|D(?:oubleBuffer|epthSize)|PixelBuffer|VirtualScreenCount|FullScreen|Window|A(?:cc(?:umSize|elerated)|ux(?:Buffers|DepthStencil)|l(?:phaSize|lRenderers))))|StepUnicodeReservedBase)|rationNotSupportedForKeyS(?:criptError|pecifierError))|ffState|KButton|rPredicateType|bjC(?:B(?:itfield|oolType)|S(?:hortType|tr(?:ingType|uctType)|electorType)|NoType|CharType|ObjectType|DoubleType|UnionType|PointerType|VoidType|FloatType|Long(?:Type|longType)|ArrayType))|D(?:i(?:s(?:c(?:losureBezelStyle|reteCapacityLevelIndicatorStyle)|playWindowRunLoopOrdering)|acriticInsensitivePredicateOption|rect(?:Selection|PredicateModifier))|o(?:c(?:ModalWindowMask|ument(?:Directory|ationDirectory))|ubleType|wn(?:TextMovement|ArrowFunctionKey))|e(?:s(?:cendingPageOrder|ktopDirectory)|cimalTabStopType|v(?:ice(?:NColorSpaceModel|IndependentModifierFlagsMask)|eloper(?:Directory|ApplicationDirectory))|fault(?:ControlTint|TokenStyle)|lete(?:Char(?:acter|FunctionKey)|FunctionKey|LineFunctionKey)|moApplicationDirectory)|a(?:yCalendarUnit|teFormatter(?:MediumStyle|Behavior(?:10|Default)|ShortStyle|NoStyle|FullStyle|LongStyle))|ra(?:wer(?:Clos(?:ingState|edState)|Open(?:ingState|State))|gOperation(?:Generic|Move|None|Copy|Delete|Private|Every|Link|All)))|U(?:ser(?:CancelledError|D(?:irectory|omainMask)|FunctionKey)|RL(?:Handle(?:NotLoaded|Load(?:Succeeded|InProgress|Failed))|CredentialPersistence(?:None|Permanent|ForSession))|n(?:scaledWindowMask|cachedRead|i(?:codeStringEncoding|talicFontMask|fiedTitleAndToolbarWindowMask)|d(?:o(?:CloseGroupingRunLoopOrdering|FunctionKey)|e(?:finedDateComponent|rline(?:Style(?:Single|None|Thick|Double)|Pattern(?:Solid|D(?:ot|ash(?:Dot(?:Dot)?)?)))))|known(?:ColorSpaceModel|P(?:ointingDevice|ageOrder)|KeyS(?:criptError|pecifierError))|boldFontMask)|tilityWindowMask|TF8StringEncoding|p(?:dateWindowsRunLoopOrdering|TextMovement|ArrowFunctionKey))|J(?:ustifiedTextAlignment|PEG(?:2000FileType|FileType)|apaneseEUC(?:GlyphPacking|StringEncoding))|P(?:o(?:s(?:t(?:Now|erFontMask|WhenIdle|ASAP)|iti(?:on(?:Replace|Be(?:fore|ginning)|End|After)|ve(?:IntType|DoubleType|FloatType)))|pUp(?:NoArrow|ArrowAt(?:Bottom|Center))|werOffEventType|rtraitOrientation)|NGFileType|ush(?:InCell(?:Mask)?|OnPushOffButton)|e(?:n(?:TipMask|UpperSideMask|PointingDevice|LowerSideMask)|riodic(?:Mask)?)|P(?:S(?:caleField|tatus(?:Title|Field)|aveButton)|N(?:ote(?:Title|Field)|ame(?:Title|Field))|CopiesField|TitleField|ImageButton|OptionsButton|P(?:a(?:perFeedButton|ge(?:Range(?:To|From)|ChoiceMatrix))|reviewButton)|LayoutButton)|lainTextTokenStyle|a(?:useFunctionKey|ragraphSeparatorCharacter|ge(?:DownFunctionKey|UpFunctionKey))|r(?:int(?:ing(?:ReplyLater|Success|Cancelled|Failure)|ScreenFunctionKey|erTable(?:NotFound|OK|Error)|FunctionKey)|o(?:p(?:ertyList(?:XMLFormat|MutableContainers(?:AndLeaves)?|BinaryFormat|Immutable|OpenStepFormat)|rietaryStringEncoding)|gressIndicator(?:BarStyle|SpinningStyle|Preferred(?:SmallThickness|Thickness|LargeThickness|AquaThickness)))|e(?:ssedTab|vFunctionKey))|L(?:HeightForm|CancelButton|TitleField|ImageButton|O(?:KButton|rientationMatrix)|UnitsButton|PaperNameButton|WidthForm))|E(?:n(?:terCharacter|d(?:sWith(?:Comparison|PredicateOperatorType)|FunctionKey))|v(?:e(?:nOddWindingRule|rySubelement)|aluatedObjectExpressionType)|qualTo(?:Comparison|PredicateOperatorType)|ra(?:serPointingDevice|CalendarUnit|DatePickerElementFlag)|x(?:clude(?:10|QuickDrawElementsIconCreationOption)|pandedFontMask|ecuteFunctionKey))|V(?:i(?:ew(?:M(?:in(?:XMargin|YMargin)|ax(?:XMargin|YMargin))|HeightSizable|NotSizable|WidthSizable)|aPanelFontAction)|erticalRuler|a(?:lidationErrorM(?:inimum|aximum)|riableExpressionType))|Key(?:SpecifierEvaluationScriptError|Down(?:Mask)?|Up(?:Mask)?|PathExpressionType|Value(?:MinusSetMutation|SetSetMutation|Change(?:Re(?:placement|moval)|Setting|Insertion)|IntersectSetMutation|ObservingOption(?:New|Old)|UnionSetMutation|ValidationError))|QTMovie(?:NormalPlayback|Looping(?:BackAndForthPlayback|Playback))|F(?:1(?:1FunctionKey|7FunctionKey|2FunctionKey|8FunctionKey|3FunctionKey|9FunctionKey|4FunctionKey|5FunctionKey|FunctionKey|0FunctionKey|6FunctionKey)|7FunctionKey|i(?:nd(?:PanelAction(?:Replace(?:A(?:ndFind|ll(?:InSelection)?))?|S(?:howFindPanel|e(?:tFindString|lectAll(?:InSelection)?))|Next|Previous)|FunctionKey)|tPagination|le(?:Read(?:No(?:SuchFileError|PermissionError)|CorruptFileError|In(?:validFileNameError|applicableStringEncodingError)|Un(?:supportedSchemeError|knownError))|HandlingPanel(?:CancelButton|OKButton)|NoSuchFileError|ErrorM(?:inimum|aximum)|Write(?:NoPermissionError|In(?:validFileNameError|applicableStringEncodingError)|OutOfSpaceError|Un(?:supportedSchemeError|knownError))|LockingError)|xedPitchFontMask)|2(?:1FunctionKey|7FunctionKey|2FunctionKey|8FunctionKey|3FunctionKey|9FunctionKey|4FunctionKey|5FunctionKey|FunctionKey|0FunctionKey|6FunctionKey)|o(?:nt(?:Mo(?:noSpaceTrait|dernSerifsClass)|BoldTrait|S(?:ymbolicClass|criptsClass|labSerifsClass|ansSerifClass)|C(?:o(?:ndensedTrait|llectionApplicationOnlyMask)|larendonSerifsClass)|TransitionalSerifsClass|I(?:ntegerAdvancementsRenderingMode|talicTrait)|O(?:ldStyleSerifsClass|rnamentalsClass)|DefaultRenderingMode|U(?:nknownClass|IOptimizedTrait)|Panel(?:S(?:hadowEffectModeMask|t(?:andardModesMask|rikethroughEffectModeMask)|izeModeMask)|CollectionModeMask|TextColorEffectModeMask|DocumentColorEffectModeMask|UnderlineEffectModeMask|FaceModeMask|All(?:ModesMask|EffectsModeMask))|ExpandedTrait|VerticalTrait|F(?:amilyClassMask|reeformSerifsClass)|Antialiased(?:RenderingMode|IntegerAdvancementsRenderingMode))|cusRing(?:Below|Type(?:None|Default|Exterior)|Only|Above)|urByteGlyphPacking|rm(?:attingError(?:M(?:inimum|aximum))?|FeedCharacter))|8FunctionKey|unction(?:ExpressionType|KeyMask)|3(?:1FunctionKey|2FunctionKey|3FunctionKey|4FunctionKey|5FunctionKey|FunctionKey|0FunctionKey)|9FunctionKey|4FunctionKey|P(?:RevertButton|S(?:ize(?:Title|Field)|etButton)|CurrentField|Preview(?:Button|Field))|l(?:oat(?:ingPointSamplesBitmapFormat|Type)|agsChanged(?:Mask)?)|axButton|5FunctionKey|6FunctionKey)|W(?:heelModeColorPanel|indow(?:s(?:NTOperatingSystem|CP125(?:1StringEncoding|2StringEncoding|3StringEncoding|4StringEncoding|0StringEncoding)|95(?:InterfaceStyle|OperatingSystem))|M(?:iniaturizeButton|ovedEventType)|Below|CloseButton|ToolbarButton|ZoomButton|Out|DocumentIconButton|ExposedEventType|Above)|orkspaceLaunch(?:NewInstance|InhibitingBackgroundOnly|Default|PreferringClassic|WithoutA(?:ctivation|ddingToRecents)|A(?:sync|nd(?:Hide(?:Others)?|Print)|llowingClassicStartup))|eek(?:day(?:CalendarUnit|OrdinalCalendarUnit)|CalendarUnit)|a(?:ntsBidiLevels|rningAlertStyle)|r(?:itingDirection(?:RightToLeft|Natural|LeftToRight)|apCalendarComponents))|L(?:i(?:stModeMatrix|ne(?:Moves(?:Right|Down|Up|Left)|B(?:order|reakBy(?:C(?:harWrapping|lipping)|Truncating(?:Middle|Head|Tail)|WordWrapping))|S(?:eparatorCharacter|weep(?:Right|Down|Up|Left))|ToBezierPathElement|DoesntMove|arSlider)|teralSearch|kePredicateOperatorType|ghterFontAction|braryDirectory)|ocalDomainMask|e(?:ssThan(?:Comparison|OrEqualTo(?:Comparison|PredicateOperatorType)|PredicateOperatorType)|ft(?:Mouse(?:D(?:own(?:Mask)?|ragged(?:Mask)?)|Up(?:Mask)?)|T(?:ext(?:Movement|Alignment)|ab(?:sBezelBorder|StopType))|ArrowFunctionKey))|a(?:yout(?:RightToLeft|NotDone|CantFit|OutOfGlyphs|Done|LeftToRight)|ndscapeOrientation)|ABColorSpaceModel)|A(?:sc(?:iiWithDoubleByteEUCGlyphPacking|endingPageOrder)|n(?:y(?:Type|PredicateModifier|EventMask)|choredSearch|imation(?:Blocking|Nonblocking(?:Threaded)?|E(?:ffect(?:DisappearingItemDefault|Poof)|ase(?:In(?:Out)?|Out))|Linear)|dPredicateType)|t(?:Bottom|tachmentCharacter|omicWrite|Top)|SCIIStringEncoding|d(?:obe(?:GB1CharacterCollection|CNS1CharacterCollection|Japan(?:1CharacterCollection|2CharacterCollection)|Korea1CharacterCollection)|dTraitFontAction|minApplicationDirectory)|uto(?:saveOperation|Pagination)|pp(?:lication(?:SupportDirectory|D(?:irectory|e(?:fined(?:Mask)?|legateReply(?:Success|Cancel|Failure)|activatedEventType))|ActivatedEventType)|KitDefined(?:Mask)?)|l(?:ternateKeyMask|pha(?:ShiftKeyMask|NonpremultipliedBitmapFormat|FirstBitmapFormat)|ert(?:SecondButtonReturn|ThirdButtonReturn|OtherReturn|DefaultReturn|ErrorReturn|FirstButtonReturn|AlternateReturn)|l(?:ScrollerParts|DomainsMask|PredicateModifier|LibrariesDirectory|ApplicationsDirectory))|rgument(?:sWrongScriptError|EvaluationScriptError)|bove(?:Bottom|Top)|WTEventType)))(?:\\b)"},{token:"support.function.C99.c",regex:s.cFunctions},{token:n.getKeywords(),regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"punctuation.section.scope.begin.objc",regex:"\\[",next:"bracketed_content"},{token:"meta.function.objc",regex:"^(?:-|\\+)\\s*"}],constant_NSString:[{token:"constant.character.escape.objc",regex:e},{token:"invalid.illegal.unknown-escape.objc",regex:"\\\\."},{token:"string",regex:'[^"\\\\]+'},{token:"punctuation.definition.string.end",regex:'"',next:"start"}],protocol_list:[{token:"punctuation.section.scope.end.objc",regex:">",next:"start"},{token:"support.other.protocol.objc",regex:"\bNS(?:GlyphStorage|M(?:utableCopying|enuItem)|C(?:hangeSpelling|o(?:ding|pying|lorPicking(?:Custom|Default)))|T(?:oolbarItemValidations|ext(?:Input|AttachmentCell))|I(?:nputServ(?:iceProvider|erMouseTracker)|gnoreMisspelledWords)|Obj(?:CTypeSerializationCallBack|ect)|D(?:ecimalNumberBehaviors|raggingInfo)|U(?:serInterfaceValidations|RL(?:HandleClient|DownloadDelegate|ProtocolClient|AuthenticationChallengeSender))|Validated(?:ToobarItem|UserInterfaceItem)|Locking)\b"}],selectors:[{token:"support.function.any-method.name-of-parameter.objc",regex:"\\b(?:[a-zA-Z_:][\\w]*)+"},{token:"punctuation",regex:"\\)",next:"start"}],bracketed_content:[{token:"punctuation.section.scope.end.objc",regex:"]",next:"start"},{token:["support.function.any-method.objc"],regex:"(?:predicateWithFormat:| NSPredicate predicateWithFormat:)",next:"start"},{token:"support.function.any-method.objc",regex:"\\w+(?::|(?=]))",next:"start"}],bracketed_strings:[{token:"punctuation.section.scope.end.objc",regex:"]",next:"start"},{token:"keyword.operator.logical.predicate.cocoa",regex:"\\b(?:AND|OR|NOT|IN)\\b"},{token:["invalid.illegal.unknown-method.objc","punctuation.separator.arguments.objc"],regex:"\\b(\\w+)(:)"},{regex:"\\b(?:ALL|ANY|SOME|NONE)\\b",token:"constant.language.predicate.cocoa"},{regex:"\\b(?:NULL|NIL|SELF|TRUE|YES|FALSE|NO|FIRST|LAST|SIZE)\\b",token:"constant.language.predicate.cocoa"},{regex:"\\b(?:MATCHES|CONTAINS|BEGINSWITH|ENDSWITH|BETWEEN)\\b",token:"keyword.operator.comparison.predicate.cocoa"},{regex:"\\bC(?:ASEINSENSITIVE|I)\\b",token:"keyword.other.modifier.predicate.cocoa"},{regex:"\\b(?:ANYKEY|SUBQUERY|CAST|TRUEPREDICATE|FALSEPREDICATE)\\b",token:"keyword.other.predicate.cocoa"},{regex:e,token:"constant.character.escape.objc"},{regex:"\\\\.",token:"invalid.illegal.unknown-escape.objc"},{token:"string",regex:'[^"\\\\]'},{token:"punctuation.definition.string.end.objc",regex:'"',next:"predicates"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],methods:[{token:"meta.function.objc",regex:"(?=\\{|#)|;",next:"start"}]};for(var u in r)this.$rules[u]?this.$rules[u].push&&this.$rules[u].push.apply(this.$rules[u],r[u]):this.$rules[u]=r[u];this.$rules.bracketed_content=this.$rules.bracketed_content.concat(this.$rules.start,t),this.embedRules(i,"doc-",[i.getEndRule("start")])};r.inherits(u,o),t.ObjectiveCHighlightRules=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/objectivec",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/objectivec_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./objectivec_highlight_rules").ObjectiveCHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.$id="ace/mode/objectivec"}.call(u.prototype),t.Mode=u}) diff --git a/public/themes/pterodactyl/vendor/ace/mode-perl.js b/public/themes/pterodactyl/vendor/ace/mode-perl.js deleted file mode 100644 index 726f03cd8..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-perl.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/perl_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="base|constant|continue|else|elsif|for|foreach|format|goto|if|last|local|my|next|no|package|parent|redo|require|scalar|sub|unless|until|while|use|vars",t="ARGV|ENV|INC|SIG",n="getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|getpeername|setpriority|getprotoent|setprotoent|getpriority|endprotoent|getservent|setservent|endservent|sethostent|socketpair|getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|map|die|uc|lc|do",r=this.createKeywordMapper({keyword:e,"constant.language":t,"support.function":n},"identifier");this.$rules={start:[{token:"comment.doc",regex:"^=(?:begin|item)\\b",next:"block_comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0x[0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"%#|\\$#|\\.\\.\\.|\\|\\|=|>>=|<<=|<=>|&&=|=>|!~|\\^=|&=|\\|=|\\.=|x=|%=|\\/=|\\*=|\\-=|\\+=|=~|\\*\\*|\\-\\-|\\.\\.|\\|\\||&&|\\+\\+|\\->|!=|==|>=|<=|>>|<<|,|=|\\?\\:|\\^|\\||x|%|\\/|\\*|<|&|\\\\|~|!|>|\\.|\\-|\\+|\\-C|\\-b|\\-S|\\-u|\\-t|\\-p|\\-l|\\-d|\\-f|\\-g|\\-s|\\-z|\\-k|\\-e|\\-O|\\-T|\\-B|\\-M|\\-A|\\-X|\\-W|\\-c|\\-R|\\-o|\\-x|\\-w|\\-r|\\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)"},{token:"comment",regex:"#.*$"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}],block_comment:[{token:"comment.doc",regex:"^=cut\\b",next:"start"},{defaultToken:"comment.doc"}]}};r.inherits(s,i),t.PerlHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/perl",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/perl_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./perl_highlight_rules").PerlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/cstyle").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u({start:"^=(begin|item)\\b",end:"^=(cut)\\b"}),this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart="#",this.blockComment=[{start:"=begin",end:"=cut",lineStartOnly:!0},{start:"=item",end:"=cut",lineStartOnly:!0}],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[:]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/perl"}.call(a.prototype),t.Mode=a}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-php.js b/public/themes/pterodactyl/vendor/ace/mode-php.js deleted file mode 100644 index 0fc768d3c..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-php.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/php_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules","ace/mode/html_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./doc_comment_highlight_rules").DocCommentHighlightRules,o=e("./text_highlight_rules").TextHighlightRules,u=e("./html_highlight_rules").HtmlHighlightRules,a=function(){var e=s,t=i.arrayToMap("abs|acos|acosh|addcslashes|addslashes|aggregate|aggregate_info|aggregate_methods|aggregate_methods_by_list|aggregate_methods_by_regexp|aggregate_properties|aggregate_properties_by_list|aggregate_properties_by_regexp|aggregation_info|amqpconnection|amqpexchange|amqpqueue|apache_child_terminate|apache_get_modules|apache_get_version|apache_getenv|apache_lookup_uri|apache_note|apache_request_headers|apache_reset_timeout|apache_response_headers|apache_setenv|apc_add|apc_bin_dump|apc_bin_dumpfile|apc_bin_load|apc_bin_loadfile|apc_cache_info|apc_cas|apc_clear_cache|apc_compile_file|apc_dec|apc_define_constants|apc_delete|apc_delete_file|apc_exists|apc_fetch|apc_inc|apc_load_constants|apc_sma_info|apc_store|apciterator|apd_breakpoint|apd_callstack|apd_clunk|apd_continue|apd_croak|apd_dump_function_table|apd_dump_persistent_resources|apd_dump_regular_resources|apd_echo|apd_get_active_symbols|apd_set_pprof_trace|apd_set_session|apd_set_session_trace|apd_set_session_trace_socket|appenditerator|array|array_change_key_case|array_chunk|array_combine|array_count_values|array_diff|array_diff_assoc|array_diff_key|array_diff_uassoc|array_diff_ukey|array_fill|array_fill_keys|array_filter|array_flip|array_intersect|array_intersect_assoc|array_intersect_key|array_intersect_uassoc|array_intersect_ukey|array_key_exists|array_keys|array_map|array_merge|array_merge_recursive|array_multisort|array_pad|array_pop|array_product|array_push|array_rand|array_reduce|array_replace|array_replace_recursive|array_reverse|array_search|array_shift|array_slice|array_splice|array_sum|array_udiff|array_udiff_assoc|array_udiff_uassoc|array_uintersect|array_uintersect_assoc|array_uintersect_uassoc|array_unique|array_unshift|array_values|array_walk|array_walk_recursive|arrayaccess|arrayiterator|arrayobject|arsort|asin|asinh|asort|assert|assert_options|atan|atan2|atanh|audioproperties|badfunctioncallexception|badmethodcallexception|base64_decode|base64_encode|base_convert|basename|bbcode_add_element|bbcode_add_smiley|bbcode_create|bbcode_destroy|bbcode_parse|bbcode_set_arg_parser|bbcode_set_flags|bcadd|bccomp|bcdiv|bcmod|bcmul|bcompiler_load|bcompiler_load_exe|bcompiler_parse_class|bcompiler_read|bcompiler_write_class|bcompiler_write_constant|bcompiler_write_exe_footer|bcompiler_write_file|bcompiler_write_footer|bcompiler_write_function|bcompiler_write_functions_from_file|bcompiler_write_header|bcompiler_write_included_filename|bcpow|bcpowmod|bcscale|bcsqrt|bcsub|bin2hex|bind_textdomain_codeset|bindec|bindtextdomain|bson_decode|bson_encode|bumpValue|bzclose|bzcompress|bzdecompress|bzerrno|bzerror|bzerrstr|bzflush|bzopen|bzread|bzwrite|cachingiterator|cairo|cairo_create|cairo_font_face_get_type|cairo_font_face_status|cairo_font_options_create|cairo_font_options_equal|cairo_font_options_get_antialias|cairo_font_options_get_hint_metrics|cairo_font_options_get_hint_style|cairo_font_options_get_subpixel_order|cairo_font_options_hash|cairo_font_options_merge|cairo_font_options_set_antialias|cairo_font_options_set_hint_metrics|cairo_font_options_set_hint_style|cairo_font_options_set_subpixel_order|cairo_font_options_status|cairo_format_stride_for_width|cairo_image_surface_create|cairo_image_surface_create_for_data|cairo_image_surface_create_from_png|cairo_image_surface_get_data|cairo_image_surface_get_format|cairo_image_surface_get_height|cairo_image_surface_get_stride|cairo_image_surface_get_width|cairo_matrix_create_scale|cairo_matrix_create_translate|cairo_matrix_invert|cairo_matrix_multiply|cairo_matrix_rotate|cairo_matrix_transform_distance|cairo_matrix_transform_point|cairo_matrix_translate|cairo_pattern_add_color_stop_rgb|cairo_pattern_add_color_stop_rgba|cairo_pattern_create_for_surface|cairo_pattern_create_linear|cairo_pattern_create_radial|cairo_pattern_create_rgb|cairo_pattern_create_rgba|cairo_pattern_get_color_stop_count|cairo_pattern_get_color_stop_rgba|cairo_pattern_get_extend|cairo_pattern_get_filter|cairo_pattern_get_linear_points|cairo_pattern_get_matrix|cairo_pattern_get_radial_circles|cairo_pattern_get_rgba|cairo_pattern_get_surface|cairo_pattern_get_type|cairo_pattern_set_extend|cairo_pattern_set_filter|cairo_pattern_set_matrix|cairo_pattern_status|cairo_pdf_surface_create|cairo_pdf_surface_set_size|cairo_ps_get_levels|cairo_ps_level_to_string|cairo_ps_surface_create|cairo_ps_surface_dsc_begin_page_setup|cairo_ps_surface_dsc_begin_setup|cairo_ps_surface_dsc_comment|cairo_ps_surface_get_eps|cairo_ps_surface_restrict_to_level|cairo_ps_surface_set_eps|cairo_ps_surface_set_size|cairo_scaled_font_create|cairo_scaled_font_extents|cairo_scaled_font_get_ctm|cairo_scaled_font_get_font_face|cairo_scaled_font_get_font_matrix|cairo_scaled_font_get_font_options|cairo_scaled_font_get_scale_matrix|cairo_scaled_font_get_type|cairo_scaled_font_glyph_extents|cairo_scaled_font_status|cairo_scaled_font_text_extents|cairo_surface_copy_page|cairo_surface_create_similar|cairo_surface_finish|cairo_surface_flush|cairo_surface_get_content|cairo_surface_get_device_offset|cairo_surface_get_font_options|cairo_surface_get_type|cairo_surface_mark_dirty|cairo_surface_mark_dirty_rectangle|cairo_surface_set_device_offset|cairo_surface_set_fallback_resolution|cairo_surface_show_page|cairo_surface_status|cairo_surface_write_to_png|cairo_svg_surface_create|cairo_svg_surface_restrict_to_version|cairo_svg_version_to_string|cairoantialias|cairocontent|cairocontext|cairoexception|cairoextend|cairofillrule|cairofilter|cairofontface|cairofontoptions|cairofontslant|cairofonttype|cairofontweight|cairoformat|cairogradientpattern|cairohintmetrics|cairohintstyle|cairoimagesurface|cairolineargradient|cairolinecap|cairolinejoin|cairomatrix|cairooperator|cairopath|cairopattern|cairopatterntype|cairopdfsurface|cairopslevel|cairopssurface|cairoradialgradient|cairoscaledfont|cairosolidpattern|cairostatus|cairosubpixelorder|cairosurface|cairosurfacepattern|cairosurfacetype|cairosvgsurface|cairosvgversion|cairotoyfontface|cal_days_in_month|cal_from_jd|cal_info|cal_to_jd|calcul_hmac|calculhmac|call_user_func|call_user_func_array|call_user_method|call_user_method_array|callbackfilteriterator|ceil|chdb|chdb_create|chdir|checkdate|checkdnsrr|chgrp|chmod|chop|chown|chr|chroot|chunk_split|class_alias|class_exists|class_implements|class_parents|class_uses|classkit_import|classkit_method_add|classkit_method_copy|classkit_method_redefine|classkit_method_remove|classkit_method_rename|clearstatcache|clone|closedir|closelog|collator|com|com_addref|com_create_guid|com_event_sink|com_get|com_get_active_object|com_invoke|com_isenum|com_load|com_load_typelib|com_message_pump|com_print_typeinfo|com_propget|com_propput|com_propset|com_release|com_set|compact|connection_aborted|connection_status|connection_timeout|constant|construct|construct|construct|convert_cyr_string|convert_uudecode|convert_uuencode|copy|cos|cosh|count|count_chars|countable|counter_bump|counter_bump_value|counter_create|counter_get|counter_get_meta|counter_get_named|counter_get_value|counter_reset|counter_reset_value|crack_check|crack_closedict|crack_getlastmessage|crack_opendict|crc32|create_function|crypt|ctype_alnum|ctype_alpha|ctype_cntrl|ctype_digit|ctype_graph|ctype_lower|ctype_print|ctype_punct|ctype_space|ctype_upper|ctype_xdigit|cubrid_affected_rows|cubrid_bind|cubrid_client_encoding|cubrid_close|cubrid_close_prepare|cubrid_close_request|cubrid_col_get|cubrid_col_size|cubrid_column_names|cubrid_column_types|cubrid_commit|cubrid_connect|cubrid_connect_with_url|cubrid_current_oid|cubrid_data_seek|cubrid_db_name|cubrid_disconnect|cubrid_drop|cubrid_errno|cubrid_error|cubrid_error_code|cubrid_error_code_facility|cubrid_error_msg|cubrid_execute|cubrid_fetch|cubrid_fetch_array|cubrid_fetch_assoc|cubrid_fetch_field|cubrid_fetch_lengths|cubrid_fetch_object|cubrid_fetch_row|cubrid_field_flags|cubrid_field_len|cubrid_field_name|cubrid_field_seek|cubrid_field_table|cubrid_field_type|cubrid_free_result|cubrid_get|cubrid_get_autocommit|cubrid_get_charset|cubrid_get_class_name|cubrid_get_client_info|cubrid_get_db_parameter|cubrid_get_server_info|cubrid_insert_id|cubrid_is_instance|cubrid_list_dbs|cubrid_load_from_glo|cubrid_lob_close|cubrid_lob_export|cubrid_lob_get|cubrid_lob_send|cubrid_lob_size|cubrid_lock_read|cubrid_lock_write|cubrid_move_cursor|cubrid_new_glo|cubrid_next_result|cubrid_num_cols|cubrid_num_fields|cubrid_num_rows|cubrid_ping|cubrid_prepare|cubrid_put|cubrid_query|cubrid_real_escape_string|cubrid_result|cubrid_rollback|cubrid_save_to_glo|cubrid_schema|cubrid_send_glo|cubrid_seq_drop|cubrid_seq_insert|cubrid_seq_put|cubrid_set_add|cubrid_set_autocommit|cubrid_set_db_parameter|cubrid_set_drop|cubrid_unbuffered_query|cubrid_version|curl_close|curl_copy_handle|curl_errno|curl_error|curl_exec|curl_getinfo|curl_init|curl_multi_add_handle|curl_multi_close|curl_multi_exec|curl_multi_getcontent|curl_multi_info_read|curl_multi_init|curl_multi_remove_handle|curl_multi_select|curl_setopt|curl_setopt_array|curl_version|current|cyrus_authenticate|cyrus_bind|cyrus_close|cyrus_connect|cyrus_query|cyrus_unbind|date|date_add|date_create|date_create_from_format|date_date_set|date_default_timezone_get|date_default_timezone_set|date_diff|date_format|date_get_last_errors|date_interval_create_from_date_string|date_interval_format|date_isodate_set|date_modify|date_offset_get|date_parse|date_parse_from_format|date_sub|date_sun_info|date_sunrise|date_sunset|date_time_set|date_timestamp_get|date_timestamp_set|date_timezone_get|date_timezone_set|dateinterval|dateperiod|datetime|datetimezone|db2_autocommit|db2_bind_param|db2_client_info|db2_close|db2_column_privileges|db2_columns|db2_commit|db2_conn_error|db2_conn_errormsg|db2_connect|db2_cursor_type|db2_escape_string|db2_exec|db2_execute|db2_fetch_array|db2_fetch_assoc|db2_fetch_both|db2_fetch_object|db2_fetch_row|db2_field_display_size|db2_field_name|db2_field_num|db2_field_precision|db2_field_scale|db2_field_type|db2_field_width|db2_foreign_keys|db2_free_result|db2_free_stmt|db2_get_option|db2_last_insert_id|db2_lob_read|db2_next_result|db2_num_fields|db2_num_rows|db2_pclose|db2_pconnect|db2_prepare|db2_primary_keys|db2_procedure_columns|db2_procedures|db2_result|db2_rollback|db2_server_info|db2_set_option|db2_special_columns|db2_statistics|db2_stmt_error|db2_stmt_errormsg|db2_table_privileges|db2_tables|dba_close|dba_delete|dba_exists|dba_fetch|dba_firstkey|dba_handlers|dba_insert|dba_key_split|dba_list|dba_nextkey|dba_open|dba_optimize|dba_popen|dba_replace|dba_sync|dbase_add_record|dbase_close|dbase_create|dbase_delete_record|dbase_get_header_info|dbase_get_record|dbase_get_record_with_names|dbase_numfields|dbase_numrecords|dbase_open|dbase_pack|dbase_replace_record|dbplus_add|dbplus_aql|dbplus_chdir|dbplus_close|dbplus_curr|dbplus_errcode|dbplus_errno|dbplus_find|dbplus_first|dbplus_flush|dbplus_freealllocks|dbplus_freelock|dbplus_freerlocks|dbplus_getlock|dbplus_getunique|dbplus_info|dbplus_last|dbplus_lockrel|dbplus_next|dbplus_open|dbplus_prev|dbplus_rchperm|dbplus_rcreate|dbplus_rcrtexact|dbplus_rcrtlike|dbplus_resolve|dbplus_restorepos|dbplus_rkeys|dbplus_ropen|dbplus_rquery|dbplus_rrename|dbplus_rsecindex|dbplus_runlink|dbplus_rzap|dbplus_savepos|dbplus_setindex|dbplus_setindexbynumber|dbplus_sql|dbplus_tcl|dbplus_tremove|dbplus_undo|dbplus_undoprepare|dbplus_unlockrel|dbplus_unselect|dbplus_update|dbplus_xlockrel|dbplus_xunlockrel|dbx_close|dbx_compare|dbx_connect|dbx_error|dbx_escape_string|dbx_fetch_row|dbx_query|dbx_sort|dcgettext|dcngettext|deaggregate|debug_backtrace|debug_print_backtrace|debug_zval_dump|decbin|dechex|decoct|define|define_syslog_variables|defined|deg2rad|delete|dgettext|die|dio_close|dio_fcntl|dio_open|dio_read|dio_seek|dio_stat|dio_tcsetattr|dio_truncate|dio_write|dir|directoryiterator|dirname|disk_free_space|disk_total_space|diskfreespace|dl|dngettext|dns_check_record|dns_get_mx|dns_get_record|dom_import_simplexml|domainexception|domattr|domattribute_name|domattribute_set_value|domattribute_specified|domattribute_value|domcharacterdata|domcomment|domdocument|domdocument_add_root|domdocument_create_attribute|domdocument_create_cdata_section|domdocument_create_comment|domdocument_create_element|domdocument_create_element_ns|domdocument_create_entity_reference|domdocument_create_processing_instruction|domdocument_create_text_node|domdocument_doctype|domdocument_document_element|domdocument_dump_file|domdocument_dump_mem|domdocument_get_element_by_id|domdocument_get_elements_by_tagname|domdocument_html_dump_mem|domdocument_xinclude|domdocumentfragment|domdocumenttype|domdocumenttype_entities|domdocumenttype_internal_subset|domdocumenttype_name|domdocumenttype_notations|domdocumenttype_public_id|domdocumenttype_system_id|domelement|domelement_get_attribute|domelement_get_attribute_node|domelement_get_elements_by_tagname|domelement_has_attribute|domelement_remove_attribute|domelement_set_attribute|domelement_set_attribute_node|domelement_tagname|domentity|domentityreference|domexception|domimplementation|domnamednodemap|domnode|domnode_add_namespace|domnode_append_child|domnode_append_sibling|domnode_attributes|domnode_child_nodes|domnode_clone_node|domnode_dump_node|domnode_first_child|domnode_get_content|domnode_has_attributes|domnode_has_child_nodes|domnode_insert_before|domnode_is_blank_node|domnode_last_child|domnode_next_sibling|domnode_node_name|domnode_node_type|domnode_node_value|domnode_owner_document|domnode_parent_node|domnode_prefix|domnode_previous_sibling|domnode_remove_child|domnode_replace_child|domnode_replace_node|domnode_set_content|domnode_set_name|domnode_set_namespace|domnode_unlink_node|domnodelist|domnotation|domprocessinginstruction|domprocessinginstruction_data|domprocessinginstruction_target|domtext|domxml_new_doc|domxml_open_file|domxml_open_mem|domxml_version|domxml_xmltree|domxml_xslt_stylesheet|domxml_xslt_stylesheet_doc|domxml_xslt_stylesheet_file|domxml_xslt_version|domxpath|domxsltstylesheet_process|domxsltstylesheet_result_dump_file|domxsltstylesheet_result_dump_mem|dotnet|dotnet_load|doubleval|each|easter_date|easter_days|echo|empty|emptyiterator|enchant_broker_describe|enchant_broker_dict_exists|enchant_broker_free|enchant_broker_free_dict|enchant_broker_get_error|enchant_broker_init|enchant_broker_list_dicts|enchant_broker_request_dict|enchant_broker_request_pwl_dict|enchant_broker_set_ordering|enchant_dict_add_to_personal|enchant_dict_add_to_session|enchant_dict_check|enchant_dict_describe|enchant_dict_get_error|enchant_dict_is_in_session|enchant_dict_quick_check|enchant_dict_store_replacement|enchant_dict_suggest|end|ereg|ereg_replace|eregi|eregi_replace|error_get_last|error_log|error_reporting|errorexception|escapeshellarg|escapeshellcmd|eval|event_add|event_base_free|event_base_loop|event_base_loopbreak|event_base_loopexit|event_base_new|event_base_priority_init|event_base_set|event_buffer_base_set|event_buffer_disable|event_buffer_enable|event_buffer_fd_set|event_buffer_free|event_buffer_new|event_buffer_priority_set|event_buffer_read|event_buffer_set_callback|event_buffer_timeout_set|event_buffer_watermark_set|event_buffer_write|event_del|event_free|event_new|event_set|exception|exec|exif_imagetype|exif_read_data|exif_tagname|exif_thumbnail|exit|exp|expect_expectl|expect_popen|explode|expm1|export|export|extension_loaded|extract|ezmlm_hash|fam_cancel_monitor|fam_close|fam_monitor_collection|fam_monitor_directory|fam_monitor_file|fam_next_event|fam_open|fam_pending|fam_resume_monitor|fam_suspend_monitor|fbsql_affected_rows|fbsql_autocommit|fbsql_blob_size|fbsql_change_user|fbsql_clob_size|fbsql_close|fbsql_commit|fbsql_connect|fbsql_create_blob|fbsql_create_clob|fbsql_create_db|fbsql_data_seek|fbsql_database|fbsql_database_password|fbsql_db_query|fbsql_db_status|fbsql_drop_db|fbsql_errno|fbsql_error|fbsql_fetch_array|fbsql_fetch_assoc|fbsql_fetch_field|fbsql_fetch_lengths|fbsql_fetch_object|fbsql_fetch_row|fbsql_field_flags|fbsql_field_len|fbsql_field_name|fbsql_field_seek|fbsql_field_table|fbsql_field_type|fbsql_free_result|fbsql_get_autostart_info|fbsql_hostname|fbsql_insert_id|fbsql_list_dbs|fbsql_list_fields|fbsql_list_tables|fbsql_next_result|fbsql_num_fields|fbsql_num_rows|fbsql_password|fbsql_pconnect|fbsql_query|fbsql_read_blob|fbsql_read_clob|fbsql_result|fbsql_rollback|fbsql_rows_fetched|fbsql_select_db|fbsql_set_characterset|fbsql_set_lob_mode|fbsql_set_password|fbsql_set_transaction|fbsql_start_db|fbsql_stop_db|fbsql_table_name|fbsql_tablename|fbsql_username|fbsql_warnings|fclose|fdf_add_doc_javascript|fdf_add_template|fdf_close|fdf_create|fdf_enum_values|fdf_errno|fdf_error|fdf_get_ap|fdf_get_attachment|fdf_get_encoding|fdf_get_file|fdf_get_flags|fdf_get_opt|fdf_get_status|fdf_get_value|fdf_get_version|fdf_header|fdf_next_field_name|fdf_open|fdf_open_string|fdf_remove_item|fdf_save|fdf_save_string|fdf_set_ap|fdf_set_encoding|fdf_set_file|fdf_set_flags|fdf_set_javascript_action|fdf_set_on_import_javascript|fdf_set_opt|fdf_set_status|fdf_set_submit_form_action|fdf_set_target_frame|fdf_set_value|fdf_set_version|feof|fflush|fgetc|fgetcsv|fgets|fgetss|file|file_exists|file_get_contents|file_put_contents|fileatime|filectime|filegroup|fileinode|filemtime|fileowner|fileperms|filepro|filepro_fieldcount|filepro_fieldname|filepro_fieldtype|filepro_fieldwidth|filepro_retrieve|filepro_rowcount|filesize|filesystemiterator|filetype|filter_has_var|filter_id|filter_input|filter_input_array|filter_list|filter_var|filter_var_array|filteriterator|finfo_buffer|finfo_close|finfo_file|finfo_open|finfo_set_flags|floatval|flock|floor|flush|fmod|fnmatch|fopen|forward_static_call|forward_static_call_array|fpassthru|fprintf|fputcsv|fputs|fread|frenchtojd|fribidi_log2vis|fscanf|fseek|fsockopen|fstat|ftell|ftok|ftp_alloc|ftp_cdup|ftp_chdir|ftp_chmod|ftp_close|ftp_connect|ftp_delete|ftp_exec|ftp_fget|ftp_fput|ftp_get|ftp_get_option|ftp_login|ftp_mdtm|ftp_mkdir|ftp_nb_continue|ftp_nb_fget|ftp_nb_fput|ftp_nb_get|ftp_nb_put|ftp_nlist|ftp_pasv|ftp_put|ftp_pwd|ftp_quit|ftp_raw|ftp_rawlist|ftp_rename|ftp_rmdir|ftp_set_option|ftp_site|ftp_size|ftp_ssl_connect|ftp_systype|ftruncate|func_get_arg|func_get_args|func_num_args|function_exists|fwrite|gc_collect_cycles|gc_disable|gc_enable|gc_enabled|gd_info|gearmanclient|gearmanjob|gearmantask|gearmanworker|geoip_continent_code_by_name|geoip_country_code3_by_name|geoip_country_code_by_name|geoip_country_name_by_name|geoip_database_info|geoip_db_avail|geoip_db_filename|geoip_db_get_all_info|geoip_id_by_name|geoip_isp_by_name|geoip_org_by_name|geoip_record_by_name|geoip_region_by_name|geoip_region_name_by_code|geoip_time_zone_by_country_and_region|getMeta|getNamed|getValue|get_browser|get_called_class|get_cfg_var|get_class|get_class_methods|get_class_vars|get_current_user|get_declared_classes|get_declared_interfaces|get_declared_traits|get_defined_constants|get_defined_functions|get_defined_vars|get_extension_funcs|get_headers|get_html_translation_table|get_include_path|get_included_files|get_loaded_extensions|get_magic_quotes_gpc|get_magic_quotes_runtime|get_meta_tags|get_object_vars|get_parent_class|get_required_files|get_resource_type|getallheaders|getconstant|getconstants|getconstructor|getcwd|getdate|getdefaultproperties|getdoccomment|getendline|getenv|getextension|getextensionname|getfilename|gethostbyaddr|gethostbyname|gethostbynamel|gethostname|getimagesize|getinterfacenames|getinterfaces|getlastmod|getmethod|getmethods|getmodifiers|getmxrr|getmygid|getmyinode|getmypid|getmyuid|getname|getnamespacename|getopt|getparentclass|getproperties|getproperty|getprotobyname|getprotobynumber|getrandmax|getrusage|getservbyname|getservbyport|getshortname|getstartline|getstaticproperties|getstaticpropertyvalue|gettext|gettimeofday|gettype|glob|globiterator|gmagick|gmagickdraw|gmagickpixel|gmdate|gmmktime|gmp_abs|gmp_add|gmp_and|gmp_clrbit|gmp_cmp|gmp_com|gmp_div|gmp_div_q|gmp_div_qr|gmp_div_r|gmp_divexact|gmp_fact|gmp_gcd|gmp_gcdext|gmp_hamdist|gmp_init|gmp_intval|gmp_invert|gmp_jacobi|gmp_legendre|gmp_mod|gmp_mul|gmp_neg|gmp_nextprime|gmp_or|gmp_perfect_square|gmp_popcount|gmp_pow|gmp_powm|gmp_prob_prime|gmp_random|gmp_scan0|gmp_scan1|gmp_setbit|gmp_sign|gmp_sqrt|gmp_sqrtrem|gmp_strval|gmp_sub|gmp_testbit|gmp_xor|gmstrftime|gnupg_adddecryptkey|gnupg_addencryptkey|gnupg_addsignkey|gnupg_cleardecryptkeys|gnupg_clearencryptkeys|gnupg_clearsignkeys|gnupg_decrypt|gnupg_decryptverify|gnupg_encrypt|gnupg_encryptsign|gnupg_export|gnupg_geterror|gnupg_getprotocol|gnupg_import|gnupg_init|gnupg_keyinfo|gnupg_setarmor|gnupg_seterrormode|gnupg_setsignmode|gnupg_sign|gnupg_verify|gopher_parsedir|grapheme_extract|grapheme_stripos|grapheme_stristr|grapheme_strlen|grapheme_strpos|grapheme_strripos|grapheme_strrpos|grapheme_strstr|grapheme_substr|gregoriantojd|gupnp_context_get_host_ip|gupnp_context_get_port|gupnp_context_get_subscription_timeout|gupnp_context_host_path|gupnp_context_new|gupnp_context_set_subscription_timeout|gupnp_context_timeout_add|gupnp_context_unhost_path|gupnp_control_point_browse_start|gupnp_control_point_browse_stop|gupnp_control_point_callback_set|gupnp_control_point_new|gupnp_device_action_callback_set|gupnp_device_info_get|gupnp_device_info_get_service|gupnp_root_device_get_available|gupnp_root_device_get_relative_location|gupnp_root_device_new|gupnp_root_device_set_available|gupnp_root_device_start|gupnp_root_device_stop|gupnp_service_action_get|gupnp_service_action_return|gupnp_service_action_return_error|gupnp_service_action_set|gupnp_service_freeze_notify|gupnp_service_info_get|gupnp_service_info_get_introspection|gupnp_service_introspection_get_state_variable|gupnp_service_notify|gupnp_service_proxy_action_get|gupnp_service_proxy_action_set|gupnp_service_proxy_add_notify|gupnp_service_proxy_callback_set|gupnp_service_proxy_get_subscribed|gupnp_service_proxy_remove_notify|gupnp_service_proxy_set_subscribed|gupnp_service_thaw_notify|gzclose|gzcompress|gzdecode|gzdeflate|gzencode|gzeof|gzfile|gzgetc|gzgets|gzgetss|gzinflate|gzopen|gzpassthru|gzputs|gzread|gzrewind|gzseek|gztell|gzuncompress|gzwrite|halt_compiler|haruannotation|haruannotation_setborderstyle|haruannotation_sethighlightmode|haruannotation_seticon|haruannotation_setopened|harudestination|harudestination_setfit|harudestination_setfitb|harudestination_setfitbh|harudestination_setfitbv|harudestination_setfith|harudestination_setfitr|harudestination_setfitv|harudestination_setxyz|harudoc|harudoc_addpage|harudoc_addpagelabel|harudoc_construct|harudoc_createoutline|harudoc_getcurrentencoder|harudoc_getcurrentpage|harudoc_getencoder|harudoc_getfont|harudoc_getinfoattr|harudoc_getpagelayout|harudoc_getpagemode|harudoc_getstreamsize|harudoc_insertpage|harudoc_loadjpeg|harudoc_loadpng|harudoc_loadraw|harudoc_loadttc|harudoc_loadttf|harudoc_loadtype1|harudoc_output|harudoc_readfromstream|harudoc_reseterror|harudoc_resetstream|harudoc_save|harudoc_savetostream|harudoc_setcompressionmode|harudoc_setcurrentencoder|harudoc_setencryptionmode|harudoc_setinfoattr|harudoc_setinfodateattr|harudoc_setopenaction|harudoc_setpagelayout|harudoc_setpagemode|harudoc_setpagesconfiguration|harudoc_setpassword|harudoc_setpermission|harudoc_usecnsencodings|harudoc_usecnsfonts|harudoc_usecntencodings|harudoc_usecntfonts|harudoc_usejpencodings|harudoc_usejpfonts|harudoc_usekrencodings|harudoc_usekrfonts|haruencoder|haruencoder_getbytetype|haruencoder_gettype|haruencoder_getunicode|haruencoder_getwritingmode|haruexception|harufont|harufont_getascent|harufont_getcapheight|harufont_getdescent|harufont_getencodingname|harufont_getfontname|harufont_gettextwidth|harufont_getunicodewidth|harufont_getxheight|harufont_measuretext|haruimage|haruimage_getbitspercomponent|haruimage_getcolorspace|haruimage_getheight|haruimage_getsize|haruimage_getwidth|haruimage_setcolormask|haruimage_setmaskimage|haruoutline|haruoutline_setdestination|haruoutline_setopened|harupage|harupage_arc|harupage_begintext|harupage_circle|harupage_closepath|harupage_concat|harupage_createdestination|harupage_createlinkannotation|harupage_createtextannotation|harupage_createurlannotation|harupage_curveto|harupage_curveto2|harupage_curveto3|harupage_drawimage|harupage_ellipse|harupage_endpath|harupage_endtext|harupage_eofill|harupage_eofillstroke|harupage_fill|harupage_fillstroke|harupage_getcharspace|harupage_getcmykfill|harupage_getcmykstroke|harupage_getcurrentfont|harupage_getcurrentfontsize|harupage_getcurrentpos|harupage_getcurrenttextpos|harupage_getdash|harupage_getfillingcolorspace|harupage_getflatness|harupage_getgmode|harupage_getgrayfill|harupage_getgraystroke|harupage_getheight|harupage_gethorizontalscaling|harupage_getlinecap|harupage_getlinejoin|harupage_getlinewidth|harupage_getmiterlimit|harupage_getrgbfill|harupage_getrgbstroke|harupage_getstrokingcolorspace|harupage_gettextleading|harupage_gettextmatrix|harupage_gettextrenderingmode|harupage_gettextrise|harupage_gettextwidth|harupage_gettransmatrix|harupage_getwidth|harupage_getwordspace|harupage_lineto|harupage_measuretext|harupage_movetextpos|harupage_moveto|harupage_movetonextline|harupage_rectangle|harupage_setcharspace|harupage_setcmykfill|harupage_setcmykstroke|harupage_setdash|harupage_setflatness|harupage_setfontandsize|harupage_setgrayfill|harupage_setgraystroke|harupage_setheight|harupage_sethorizontalscaling|harupage_setlinecap|harupage_setlinejoin|harupage_setlinewidth|harupage_setmiterlimit|harupage_setrgbfill|harupage_setrgbstroke|harupage_setrotate|harupage_setsize|harupage_setslideshow|harupage_settextleading|harupage_settextmatrix|harupage_settextrenderingmode|harupage_settextrise|harupage_setwidth|harupage_setwordspace|harupage_showtext|harupage_showtextnextline|harupage_stroke|harupage_textout|harupage_textrect|hasconstant|hash|hash_algos|hash_copy|hash_file|hash_final|hash_hmac|hash_hmac_file|hash_init|hash_update|hash_update_file|hash_update_stream|hasmethod|hasproperty|header|header_register_callback|header_remove|headers_list|headers_sent|hebrev|hebrevc|hex2bin|hexdec|highlight_file|highlight_string|html_entity_decode|htmlentities|htmlspecialchars|htmlspecialchars_decode|http_build_cookie|http_build_query|http_build_str|http_build_url|http_cache_etag|http_cache_last_modified|http_chunked_decode|http_date|http_deflate|http_get|http_get_request_body|http_get_request_body_stream|http_get_request_headers|http_head|http_inflate|http_match_etag|http_match_modified|http_match_request_header|http_negotiate_charset|http_negotiate_content_type|http_negotiate_language|http_parse_cookie|http_parse_headers|http_parse_message|http_parse_params|http_persistent_handles_clean|http_persistent_handles_count|http_persistent_handles_ident|http_post_data|http_post_fields|http_put_data|http_put_file|http_put_stream|http_redirect|http_request|http_request_body_encode|http_request_method_exists|http_request_method_name|http_request_method_register|http_request_method_unregister|http_response_code|http_send_content_disposition|http_send_content_type|http_send_data|http_send_file|http_send_last_modified|http_send_status|http_send_stream|http_support|http_throttle|httpdeflatestream|httpdeflatestream_construct|httpdeflatestream_factory|httpdeflatestream_finish|httpdeflatestream_flush|httpdeflatestream_update|httpinflatestream|httpinflatestream_construct|httpinflatestream_factory|httpinflatestream_finish|httpinflatestream_flush|httpinflatestream_update|httpmessage|httpmessage_addheaders|httpmessage_construct|httpmessage_detach|httpmessage_factory|httpmessage_fromenv|httpmessage_fromstring|httpmessage_getbody|httpmessage_getheader|httpmessage_getheaders|httpmessage_gethttpversion|httpmessage_getparentmessage|httpmessage_getrequestmethod|httpmessage_getrequesturl|httpmessage_getresponsecode|httpmessage_getresponsestatus|httpmessage_gettype|httpmessage_guesscontenttype|httpmessage_prepend|httpmessage_reverse|httpmessage_send|httpmessage_setbody|httpmessage_setheaders|httpmessage_sethttpversion|httpmessage_setrequestmethod|httpmessage_setrequesturl|httpmessage_setresponsecode|httpmessage_setresponsestatus|httpmessage_settype|httpmessage_tomessagetypeobject|httpmessage_tostring|httpquerystring|httpquerystring_construct|httpquerystring_get|httpquerystring_mod|httpquerystring_set|httpquerystring_singleton|httpquerystring_toarray|httpquerystring_tostring|httpquerystring_xlate|httprequest|httprequest_addcookies|httprequest_addheaders|httprequest_addpostfields|httprequest_addpostfile|httprequest_addputdata|httprequest_addquerydata|httprequest_addrawpostdata|httprequest_addssloptions|httprequest_clearhistory|httprequest_construct|httprequest_enablecookies|httprequest_getcontenttype|httprequest_getcookies|httprequest_getheaders|httprequest_gethistory|httprequest_getmethod|httprequest_getoptions|httprequest_getpostfields|httprequest_getpostfiles|httprequest_getputdata|httprequest_getputfile|httprequest_getquerydata|httprequest_getrawpostdata|httprequest_getrawrequestmessage|httprequest_getrawresponsemessage|httprequest_getrequestmessage|httprequest_getresponsebody|httprequest_getresponsecode|httprequest_getresponsecookies|httprequest_getresponsedata|httprequest_getresponseheader|httprequest_getresponseinfo|httprequest_getresponsemessage|httprequest_getresponsestatus|httprequest_getssloptions|httprequest_geturl|httprequest_resetcookies|httprequest_send|httprequest_setcontenttype|httprequest_setcookies|httprequest_setheaders|httprequest_setmethod|httprequest_setoptions|httprequest_setpostfields|httprequest_setpostfiles|httprequest_setputdata|httprequest_setputfile|httprequest_setquerydata|httprequest_setrawpostdata|httprequest_setssloptions|httprequest_seturl|httprequestpool|httprequestpool_attach|httprequestpool_construct|httprequestpool_destruct|httprequestpool_detach|httprequestpool_getattachedrequests|httprequestpool_getfinishedrequests|httprequestpool_reset|httprequestpool_send|httprequestpool_socketperform|httprequestpool_socketselect|httpresponse|httpresponse_capture|httpresponse_getbuffersize|httpresponse_getcache|httpresponse_getcachecontrol|httpresponse_getcontentdisposition|httpresponse_getcontenttype|httpresponse_getdata|httpresponse_getetag|httpresponse_getfile|httpresponse_getgzip|httpresponse_getheader|httpresponse_getlastmodified|httpresponse_getrequestbody|httpresponse_getrequestbodystream|httpresponse_getrequestheaders|httpresponse_getstream|httpresponse_getthrottledelay|httpresponse_guesscontenttype|httpresponse_redirect|httpresponse_send|httpresponse_setbuffersize|httpresponse_setcache|httpresponse_setcachecontrol|httpresponse_setcontentdisposition|httpresponse_setcontenttype|httpresponse_setdata|httpresponse_setetag|httpresponse_setfile|httpresponse_setgzip|httpresponse_setheader|httpresponse_setlastmodified|httpresponse_setstream|httpresponse_setthrottledelay|httpresponse_status|hw_array2objrec|hw_changeobject|hw_children|hw_childrenobj|hw_close|hw_connect|hw_connection_info|hw_cp|hw_deleteobject|hw_docbyanchor|hw_docbyanchorobj|hw_document_attributes|hw_document_bodytag|hw_document_content|hw_document_setcontent|hw_document_size|hw_dummy|hw_edittext|hw_error|hw_errormsg|hw_free_document|hw_getanchors|hw_getanchorsobj|hw_getandlock|hw_getchildcoll|hw_getchildcollobj|hw_getchilddoccoll|hw_getchilddoccollobj|hw_getobject|hw_getobjectbyquery|hw_getobjectbyquerycoll|hw_getobjectbyquerycollobj|hw_getobjectbyqueryobj|hw_getparents|hw_getparentsobj|hw_getrellink|hw_getremote|hw_getremotechildren|hw_getsrcbydestobj|hw_gettext|hw_getusername|hw_identify|hw_incollections|hw_info|hw_inscoll|hw_insdoc|hw_insertanchors|hw_insertdocument|hw_insertobject|hw_mapid|hw_modifyobject|hw_mv|hw_new_document|hw_objrec2array|hw_output_document|hw_pconnect|hw_pipedocument|hw_root|hw_setlinkroot|hw_stat|hw_unlock|hw_who|hwapi_attribute|hwapi_attribute_key|hwapi_attribute_langdepvalue|hwapi_attribute_value|hwapi_attribute_values|hwapi_checkin|hwapi_checkout|hwapi_children|hwapi_content|hwapi_content_mimetype|hwapi_content_read|hwapi_copy|hwapi_dbstat|hwapi_dcstat|hwapi_dstanchors|hwapi_dstofsrcanchor|hwapi_error_count|hwapi_error_reason|hwapi_find|hwapi_ftstat|hwapi_hgcsp|hwapi_hwstat|hwapi_identify|hwapi_info|hwapi_insert|hwapi_insertanchor|hwapi_insertcollection|hwapi_insertdocument|hwapi_link|hwapi_lock|hwapi_move|hwapi_new_content|hwapi_object|hwapi_object_assign|hwapi_object_attreditable|hwapi_object_count|hwapi_object_insert|hwapi_object_new|hwapi_object_remove|hwapi_object_title|hwapi_object_value|hwapi_objectbyanchor|hwapi_parents|hwapi_reason_description|hwapi_reason_type|hwapi_remove|hwapi_replace|hwapi_setcommittedversion|hwapi_srcanchors|hwapi_srcsofdst|hwapi_unlock|hwapi_user|hwapi_userlist|hypot|ibase_add_user|ibase_affected_rows|ibase_backup|ibase_blob_add|ibase_blob_cancel|ibase_blob_close|ibase_blob_create|ibase_blob_echo|ibase_blob_get|ibase_blob_import|ibase_blob_info|ibase_blob_open|ibase_close|ibase_commit|ibase_commit_ret|ibase_connect|ibase_db_info|ibase_delete_user|ibase_drop_db|ibase_errcode|ibase_errmsg|ibase_execute|ibase_fetch_assoc|ibase_fetch_object|ibase_fetch_row|ibase_field_info|ibase_free_event_handler|ibase_free_query|ibase_free_result|ibase_gen_id|ibase_maintain_db|ibase_modify_user|ibase_name_result|ibase_num_fields|ibase_num_params|ibase_param_info|ibase_pconnect|ibase_prepare|ibase_query|ibase_restore|ibase_rollback|ibase_rollback_ret|ibase_server_info|ibase_service_attach|ibase_service_detach|ibase_set_event_handler|ibase_timefmt|ibase_trans|ibase_wait_event|iconv|iconv_get_encoding|iconv_mime_decode|iconv_mime_decode_headers|iconv_mime_encode|iconv_set_encoding|iconv_strlen|iconv_strpos|iconv_strrpos|iconv_substr|id3_get_frame_long_name|id3_get_frame_short_name|id3_get_genre_id|id3_get_genre_list|id3_get_genre_name|id3_get_tag|id3_get_version|id3_remove_tag|id3_set_tag|id3v2attachedpictureframe|id3v2frame|id3v2tag|idate|idn_to_ascii|idn_to_unicode|idn_to_utf8|ifx_affected_rows|ifx_blobinfile_mode|ifx_byteasvarchar|ifx_close|ifx_connect|ifx_copy_blob|ifx_create_blob|ifx_create_char|ifx_do|ifx_error|ifx_errormsg|ifx_fetch_row|ifx_fieldproperties|ifx_fieldtypes|ifx_free_blob|ifx_free_char|ifx_free_result|ifx_get_blob|ifx_get_char|ifx_getsqlca|ifx_htmltbl_result|ifx_nullformat|ifx_num_fields|ifx_num_rows|ifx_pconnect|ifx_prepare|ifx_query|ifx_textasvarchar|ifx_update_blob|ifx_update_char|ifxus_close_slob|ifxus_create_slob|ifxus_free_slob|ifxus_open_slob|ifxus_read_slob|ifxus_seek_slob|ifxus_tell_slob|ifxus_write_slob|ignore_user_abort|iis_add_server|iis_get_dir_security|iis_get_script_map|iis_get_server_by_comment|iis_get_server_by_path|iis_get_server_rights|iis_get_service_state|iis_remove_server|iis_set_app_settings|iis_set_dir_security|iis_set_script_map|iis_set_server_rights|iis_start_server|iis_start_service|iis_stop_server|iis_stop_service|image2wbmp|image_type_to_extension|image_type_to_mime_type|imagealphablending|imageantialias|imagearc|imagechar|imagecharup|imagecolorallocate|imagecolorallocatealpha|imagecolorat|imagecolorclosest|imagecolorclosestalpha|imagecolorclosesthwb|imagecolordeallocate|imagecolorexact|imagecolorexactalpha|imagecolormatch|imagecolorresolve|imagecolorresolvealpha|imagecolorset|imagecolorsforindex|imagecolorstotal|imagecolortransparent|imageconvolution|imagecopy|imagecopymerge|imagecopymergegray|imagecopyresampled|imagecopyresized|imagecreate|imagecreatefromgd|imagecreatefromgd2|imagecreatefromgd2part|imagecreatefromgif|imagecreatefromjpeg|imagecreatefrompng|imagecreatefromstring|imagecreatefromwbmp|imagecreatefromxbm|imagecreatefromxpm|imagecreatetruecolor|imagedashedline|imagedestroy|imageellipse|imagefill|imagefilledarc|imagefilledellipse|imagefilledpolygon|imagefilledrectangle|imagefilltoborder|imagefilter|imagefontheight|imagefontwidth|imageftbbox|imagefttext|imagegammacorrect|imagegd|imagegd2|imagegif|imagegrabscreen|imagegrabwindow|imageinterlace|imageistruecolor|imagejpeg|imagelayereffect|imageline|imageloadfont|imagepalettecopy|imagepng|imagepolygon|imagepsbbox|imagepsencodefont|imagepsextendfont|imagepsfreefont|imagepsloadfont|imagepsslantfont|imagepstext|imagerectangle|imagerotate|imagesavealpha|imagesetbrush|imagesetpixel|imagesetstyle|imagesetthickness|imagesettile|imagestring|imagestringup|imagesx|imagesy|imagetruecolortopalette|imagettfbbox|imagettftext|imagetypes|imagewbmp|imagexbm|imagick|imagick_adaptiveblurimage|imagick_adaptiveresizeimage|imagick_adaptivesharpenimage|imagick_adaptivethresholdimage|imagick_addimage|imagick_addnoiseimage|imagick_affinetransformimage|imagick_animateimages|imagick_annotateimage|imagick_appendimages|imagick_averageimages|imagick_blackthresholdimage|imagick_blurimage|imagick_borderimage|imagick_charcoalimage|imagick_chopimage|imagick_clear|imagick_clipimage|imagick_clippathimage|imagick_clone|imagick_clutimage|imagick_coalesceimages|imagick_colorfloodfillimage|imagick_colorizeimage|imagick_combineimages|imagick_commentimage|imagick_compareimagechannels|imagick_compareimagelayers|imagick_compareimages|imagick_compositeimage|imagick_construct|imagick_contrastimage|imagick_contraststretchimage|imagick_convolveimage|imagick_cropimage|imagick_cropthumbnailimage|imagick_current|imagick_cyclecolormapimage|imagick_decipherimage|imagick_deconstructimages|imagick_deleteimageartifact|imagick_despeckleimage|imagick_destroy|imagick_displayimage|imagick_displayimages|imagick_distortimage|imagick_drawimage|imagick_edgeimage|imagick_embossimage|imagick_encipherimage|imagick_enhanceimage|imagick_equalizeimage|imagick_evaluateimage|imagick_extentimage|imagick_flattenimages|imagick_flipimage|imagick_floodfillpaintimage|imagick_flopimage|imagick_frameimage|imagick_fximage|imagick_gammaimage|imagick_gaussianblurimage|imagick_getcolorspace|imagick_getcompression|imagick_getcompressionquality|imagick_getcopyright|imagick_getfilename|imagick_getfont|imagick_getformat|imagick_getgravity|imagick_gethomeurl|imagick_getimage|imagick_getimagealphachannel|imagick_getimageartifact|imagick_getimagebackgroundcolor|imagick_getimageblob|imagick_getimageblueprimary|imagick_getimagebordercolor|imagick_getimagechanneldepth|imagick_getimagechanneldistortion|imagick_getimagechanneldistortions|imagick_getimagechannelextrema|imagick_getimagechannelmean|imagick_getimagechannelrange|imagick_getimagechannelstatistics|imagick_getimageclipmask|imagick_getimagecolormapcolor|imagick_getimagecolors|imagick_getimagecolorspace|imagick_getimagecompose|imagick_getimagecompression|imagick_getimagecompressionquality|imagick_getimagedelay|imagick_getimagedepth|imagick_getimagedispose|imagick_getimagedistortion|imagick_getimageextrema|imagick_getimagefilename|imagick_getimageformat|imagick_getimagegamma|imagick_getimagegeometry|imagick_getimagegravity|imagick_getimagegreenprimary|imagick_getimageheight|imagick_getimagehistogram|imagick_getimageindex|imagick_getimageinterlacescheme|imagick_getimageinterpolatemethod|imagick_getimageiterations|imagick_getimagelength|imagick_getimagemagicklicense|imagick_getimagematte|imagick_getimagemattecolor|imagick_getimageorientation|imagick_getimagepage|imagick_getimagepixelcolor|imagick_getimageprofile|imagick_getimageprofiles|imagick_getimageproperties|imagick_getimageproperty|imagick_getimageredprimary|imagick_getimageregion|imagick_getimagerenderingintent|imagick_getimageresolution|imagick_getimagesblob|imagick_getimagescene|imagick_getimagesignature|imagick_getimagesize|imagick_getimagetickspersecond|imagick_getimagetotalinkdensity|imagick_getimagetype|imagick_getimageunits|imagick_getimagevirtualpixelmethod|imagick_getimagewhitepoint|imagick_getimagewidth|imagick_getinterlacescheme|imagick_getiteratorindex|imagick_getnumberimages|imagick_getoption|imagick_getpackagename|imagick_getpage|imagick_getpixeliterator|imagick_getpixelregioniterator|imagick_getpointsize|imagick_getquantumdepth|imagick_getquantumrange|imagick_getreleasedate|imagick_getresource|imagick_getresourcelimit|imagick_getsamplingfactors|imagick_getsize|imagick_getsizeoffset|imagick_getversion|imagick_hasnextimage|imagick_haspreviousimage|imagick_identifyimage|imagick_implodeimage|imagick_labelimage|imagick_levelimage|imagick_linearstretchimage|imagick_liquidrescaleimage|imagick_magnifyimage|imagick_mapimage|imagick_mattefloodfillimage|imagick_medianfilterimage|imagick_mergeimagelayers|imagick_minifyimage|imagick_modulateimage|imagick_montageimage|imagick_morphimages|imagick_mosaicimages|imagick_motionblurimage|imagick_negateimage|imagick_newimage|imagick_newpseudoimage|imagick_nextimage|imagick_normalizeimage|imagick_oilpaintimage|imagick_opaquepaintimage|imagick_optimizeimagelayers|imagick_orderedposterizeimage|imagick_paintfloodfillimage|imagick_paintopaqueimage|imagick_painttransparentimage|imagick_pingimage|imagick_pingimageblob|imagick_pingimagefile|imagick_polaroidimage|imagick_posterizeimage|imagick_previewimages|imagick_previousimage|imagick_profileimage|imagick_quantizeimage|imagick_quantizeimages|imagick_queryfontmetrics|imagick_queryfonts|imagick_queryformats|imagick_radialblurimage|imagick_raiseimage|imagick_randomthresholdimage|imagick_readimage|imagick_readimageblob|imagick_readimagefile|imagick_recolorimage|imagick_reducenoiseimage|imagick_removeimage|imagick_removeimageprofile|imagick_render|imagick_resampleimage|imagick_resetimagepage|imagick_resizeimage|imagick_rollimage|imagick_rotateimage|imagick_roundcorners|imagick_sampleimage|imagick_scaleimage|imagick_separateimagechannel|imagick_sepiatoneimage|imagick_setbackgroundcolor|imagick_setcolorspace|imagick_setcompression|imagick_setcompressionquality|imagick_setfilename|imagick_setfirstiterator|imagick_setfont|imagick_setformat|imagick_setgravity|imagick_setimage|imagick_setimagealphachannel|imagick_setimageartifact|imagick_setimagebackgroundcolor|imagick_setimagebias|imagick_setimageblueprimary|imagick_setimagebordercolor|imagick_setimagechanneldepth|imagick_setimageclipmask|imagick_setimagecolormapcolor|imagick_setimagecolorspace|imagick_setimagecompose|imagick_setimagecompression|imagick_setimagecompressionquality|imagick_setimagedelay|imagick_setimagedepth|imagick_setimagedispose|imagick_setimageextent|imagick_setimagefilename|imagick_setimageformat|imagick_setimagegamma|imagick_setimagegravity|imagick_setimagegreenprimary|imagick_setimageindex|imagick_setimageinterlacescheme|imagick_setimageinterpolatemethod|imagick_setimageiterations|imagick_setimagematte|imagick_setimagemattecolor|imagick_setimageopacity|imagick_setimageorientation|imagick_setimagepage|imagick_setimageprofile|imagick_setimageproperty|imagick_setimageredprimary|imagick_setimagerenderingintent|imagick_setimageresolution|imagick_setimagescene|imagick_setimagetickspersecond|imagick_setimagetype|imagick_setimageunits|imagick_setimagevirtualpixelmethod|imagick_setimagewhitepoint|imagick_setinterlacescheme|imagick_setiteratorindex|imagick_setlastiterator|imagick_setoption|imagick_setpage|imagick_setpointsize|imagick_setresolution|imagick_setresourcelimit|imagick_setsamplingfactors|imagick_setsize|imagick_setsizeoffset|imagick_settype|imagick_shadeimage|imagick_shadowimage|imagick_sharpenimage|imagick_shaveimage|imagick_shearimage|imagick_sigmoidalcontrastimage|imagick_sketchimage|imagick_solarizeimage|imagick_spliceimage|imagick_spreadimage|imagick_steganoimage|imagick_stereoimage|imagick_stripimage|imagick_swirlimage|imagick_textureimage|imagick_thresholdimage|imagick_thumbnailimage|imagick_tintimage|imagick_transformimage|imagick_transparentpaintimage|imagick_transposeimage|imagick_transverseimage|imagick_trimimage|imagick_uniqueimagecolors|imagick_unsharpmaskimage|imagick_valid|imagick_vignetteimage|imagick_waveimage|imagick_whitethresholdimage|imagick_writeimage|imagick_writeimagefile|imagick_writeimages|imagick_writeimagesfile|imagickdraw|imagickdraw_affine|imagickdraw_annotation|imagickdraw_arc|imagickdraw_bezier|imagickdraw_circle|imagickdraw_clear|imagickdraw_clone|imagickdraw_color|imagickdraw_comment|imagickdraw_composite|imagickdraw_construct|imagickdraw_destroy|imagickdraw_ellipse|imagickdraw_getclippath|imagickdraw_getcliprule|imagickdraw_getclipunits|imagickdraw_getfillcolor|imagickdraw_getfillopacity|imagickdraw_getfillrule|imagickdraw_getfont|imagickdraw_getfontfamily|imagickdraw_getfontsize|imagickdraw_getfontstyle|imagickdraw_getfontweight|imagickdraw_getgravity|imagickdraw_getstrokeantialias|imagickdraw_getstrokecolor|imagickdraw_getstrokedasharray|imagickdraw_getstrokedashoffset|imagickdraw_getstrokelinecap|imagickdraw_getstrokelinejoin|imagickdraw_getstrokemiterlimit|imagickdraw_getstrokeopacity|imagickdraw_getstrokewidth|imagickdraw_gettextalignment|imagickdraw_gettextantialias|imagickdraw_gettextdecoration|imagickdraw_gettextencoding|imagickdraw_gettextundercolor|imagickdraw_getvectorgraphics|imagickdraw_line|imagickdraw_matte|imagickdraw_pathclose|imagickdraw_pathcurvetoabsolute|imagickdraw_pathcurvetoquadraticbezierabsolute|imagickdraw_pathcurvetoquadraticbezierrelative|imagickdraw_pathcurvetoquadraticbeziersmoothabsolute|imagickdraw_pathcurvetoquadraticbeziersmoothrelative|imagickdraw_pathcurvetorelative|imagickdraw_pathcurvetosmoothabsolute|imagickdraw_pathcurvetosmoothrelative|imagickdraw_pathellipticarcabsolute|imagickdraw_pathellipticarcrelative|imagickdraw_pathfinish|imagickdraw_pathlinetoabsolute|imagickdraw_pathlinetohorizontalabsolute|imagickdraw_pathlinetohorizontalrelative|imagickdraw_pathlinetorelative|imagickdraw_pathlinetoverticalabsolute|imagickdraw_pathlinetoverticalrelative|imagickdraw_pathmovetoabsolute|imagickdraw_pathmovetorelative|imagickdraw_pathstart|imagickdraw_point|imagickdraw_polygon|imagickdraw_polyline|imagickdraw_pop|imagickdraw_popclippath|imagickdraw_popdefs|imagickdraw_poppattern|imagickdraw_push|imagickdraw_pushclippath|imagickdraw_pushdefs|imagickdraw_pushpattern|imagickdraw_rectangle|imagickdraw_render|imagickdraw_rotate|imagickdraw_roundrectangle|imagickdraw_scale|imagickdraw_setclippath|imagickdraw_setcliprule|imagickdraw_setclipunits|imagickdraw_setfillalpha|imagickdraw_setfillcolor|imagickdraw_setfillopacity|imagickdraw_setfillpatternurl|imagickdraw_setfillrule|imagickdraw_setfont|imagickdraw_setfontfamily|imagickdraw_setfontsize|imagickdraw_setfontstretch|imagickdraw_setfontstyle|imagickdraw_setfontweight|imagickdraw_setgravity|imagickdraw_setstrokealpha|imagickdraw_setstrokeantialias|imagickdraw_setstrokecolor|imagickdraw_setstrokedasharray|imagickdraw_setstrokedashoffset|imagickdraw_setstrokelinecap|imagickdraw_setstrokelinejoin|imagickdraw_setstrokemiterlimit|imagickdraw_setstrokeopacity|imagickdraw_setstrokepatternurl|imagickdraw_setstrokewidth|imagickdraw_settextalignment|imagickdraw_settextantialias|imagickdraw_settextdecoration|imagickdraw_settextencoding|imagickdraw_settextundercolor|imagickdraw_setvectorgraphics|imagickdraw_setviewbox|imagickdraw_skewx|imagickdraw_skewy|imagickdraw_translate|imagickpixel|imagickpixel_clear|imagickpixel_construct|imagickpixel_destroy|imagickpixel_getcolor|imagickpixel_getcolorasstring|imagickpixel_getcolorcount|imagickpixel_getcolorvalue|imagickpixel_gethsl|imagickpixel_issimilar|imagickpixel_setcolor|imagickpixel_setcolorvalue|imagickpixel_sethsl|imagickpixeliterator|imagickpixeliterator_clear|imagickpixeliterator_construct|imagickpixeliterator_destroy|imagickpixeliterator_getcurrentiteratorrow|imagickpixeliterator_getiteratorrow|imagickpixeliterator_getnextiteratorrow|imagickpixeliterator_getpreviousiteratorrow|imagickpixeliterator_newpixeliterator|imagickpixeliterator_newpixelregioniterator|imagickpixeliterator_resetiterator|imagickpixeliterator_setiteratorfirstrow|imagickpixeliterator_setiteratorlastrow|imagickpixeliterator_setiteratorrow|imagickpixeliterator_synciterator|imap_8bit|imap_alerts|imap_append|imap_base64|imap_binary|imap_body|imap_bodystruct|imap_check|imap_clearflag_full|imap_close|imap_create|imap_createmailbox|imap_delete|imap_deletemailbox|imap_errors|imap_expunge|imap_fetch_overview|imap_fetchbody|imap_fetchheader|imap_fetchmime|imap_fetchstructure|imap_fetchtext|imap_gc|imap_get_quota|imap_get_quotaroot|imap_getacl|imap_getmailboxes|imap_getsubscribed|imap_header|imap_headerinfo|imap_headers|imap_last_error|imap_list|imap_listmailbox|imap_listscan|imap_listsubscribed|imap_lsub|imap_mail|imap_mail_compose|imap_mail_copy|imap_mail_move|imap_mailboxmsginfo|imap_mime_header_decode|imap_msgno|imap_num_msg|imap_num_recent|imap_open|imap_ping|imap_qprint|imap_rename|imap_renamemailbox|imap_reopen|imap_rfc822_parse_adrlist|imap_rfc822_parse_headers|imap_rfc822_write_address|imap_savebody|imap_scan|imap_scanmailbox|imap_search|imap_set_quota|imap_setacl|imap_setflag_full|imap_sort|imap_status|imap_subscribe|imap_thread|imap_timeout|imap_uid|imap_undelete|imap_unsubscribe|imap_utf7_decode|imap_utf7_encode|imap_utf8|implementsinterface|implode|import_request_variables|in_array|include|include_once|inclued_get_data|inet_ntop|inet_pton|infiniteiterator|ingres_autocommit|ingres_autocommit_state|ingres_charset|ingres_close|ingres_commit|ingres_connect|ingres_cursor|ingres_errno|ingres_error|ingres_errsqlstate|ingres_escape_string|ingres_execute|ingres_fetch_array|ingres_fetch_assoc|ingres_fetch_object|ingres_fetch_proc_return|ingres_fetch_row|ingres_field_length|ingres_field_name|ingres_field_nullable|ingres_field_precision|ingres_field_scale|ingres_field_type|ingres_free_result|ingres_next_error|ingres_num_fields|ingres_num_rows|ingres_pconnect|ingres_prepare|ingres_query|ingres_result_seek|ingres_rollback|ingres_set_environment|ingres_unbuffered_query|ini_alter|ini_get|ini_get_all|ini_restore|ini_set|innamespace|inotify_add_watch|inotify_init|inotify_queue_len|inotify_read|inotify_rm_watch|interface_exists|intl_error_name|intl_get_error_code|intl_get_error_message|intl_is_failure|intldateformatter|intval|invalidargumentexception|invoke|invokeargs|ip2long|iptcembed|iptcparse|is_a|is_array|is_bool|is_callable|is_dir|is_double|is_executable|is_file|is_finite|is_float|is_infinite|is_int|is_integer|is_link|is_long|is_nan|is_null|is_numeric|is_object|is_readable|is_real|is_resource|is_scalar|is_soap_fault|is_string|is_subclass_of|is_uploaded_file|is_writable|is_writeable|isabstract|iscloneable|isdisabled|isfinal|isinstance|isinstantiable|isinterface|isinternal|isiterateable|isset|issubclassof|isuserdefined|iterator|iterator_apply|iterator_count|iterator_to_array|iteratoraggregate|iteratoriterator|java_last_exception_clear|java_last_exception_get|jddayofweek|jdmonthname|jdtofrench|jdtogregorian|jdtojewish|jdtojulian|jdtounix|jewishtojd|join|jpeg2wbmp|json_decode|json_encode|json_last_error|jsonserializable|judy|judy_type|judy_version|juliantojd|kadm5_chpass_principal|kadm5_create_principal|kadm5_delete_principal|kadm5_destroy|kadm5_flush|kadm5_get_policies|kadm5_get_principal|kadm5_get_principals|kadm5_init_with_password|kadm5_modify_principal|key|krsort|ksort|lcfirst|lcg_value|lchgrp|lchown|ldap_8859_to_t61|ldap_add|ldap_bind|ldap_close|ldap_compare|ldap_connect|ldap_count_entries|ldap_delete|ldap_dn2ufn|ldap_err2str|ldap_errno|ldap_error|ldap_explode_dn|ldap_first_attribute|ldap_first_entry|ldap_first_reference|ldap_free_result|ldap_get_attributes|ldap_get_dn|ldap_get_entries|ldap_get_option|ldap_get_values|ldap_get_values_len|ldap_list|ldap_mod_add|ldap_mod_del|ldap_mod_replace|ldap_modify|ldap_next_attribute|ldap_next_entry|ldap_next_reference|ldap_parse_reference|ldap_parse_result|ldap_read|ldap_rename|ldap_sasl_bind|ldap_search|ldap_set_option|ldap_set_rebind_proc|ldap_sort|ldap_start_tls|ldap_t61_to_8859|ldap_unbind|lengthexception|levenshtein|libxml_clear_errors|libxml_disable_entity_loader|libxml_get_errors|libxml_get_last_error|libxml_set_streams_context|libxml_use_internal_errors|libxmlerror|limititerator|link|linkinfo|list|locale|localeconv|localtime|log|log10|log1p|logicexception|long2ip|lstat|ltrim|lzf_compress|lzf_decompress|lzf_optimized_for|m_checkstatus|m_completeauthorizations|m_connect|m_connectionerror|m_deletetrans|m_destroyconn|m_destroyengine|m_getcell|m_getcellbynum|m_getcommadelimited|m_getheader|m_initconn|m_initengine|m_iscommadelimited|m_maxconntimeout|m_monitor|m_numcolumns|m_numrows|m_parsecommadelimited|m_responsekeys|m_responseparam|m_returnstatus|m_setblocking|m_setdropfile|m_setip|m_setssl|m_setssl_cafile|m_setssl_files|m_settimeout|m_sslcert_gen_hash|m_transactionssent|m_transinqueue|m_transkeyval|m_transnew|m_transsend|m_uwait|m_validateidentifier|m_verifyconnection|m_verifysslcert|magic_quotes_runtime|mail|mailparse_determine_best_xfer_encoding|mailparse_msg_create|mailparse_msg_extract_part|mailparse_msg_extract_part_file|mailparse_msg_extract_whole_part_file|mailparse_msg_free|mailparse_msg_get_part|mailparse_msg_get_part_data|mailparse_msg_get_structure|mailparse_msg_parse|mailparse_msg_parse_file|mailparse_rfc822_parse_addresses|mailparse_stream_encode|mailparse_uudecode_all|main|max|maxdb_affected_rows|maxdb_autocommit|maxdb_bind_param|maxdb_bind_result|maxdb_change_user|maxdb_character_set_name|maxdb_client_encoding|maxdb_close|maxdb_close_long_data|maxdb_commit|maxdb_connect|maxdb_connect_errno|maxdb_connect_error|maxdb_data_seek|maxdb_debug|maxdb_disable_reads_from_master|maxdb_disable_rpl_parse|maxdb_dump_debug_info|maxdb_embedded_connect|maxdb_enable_reads_from_master|maxdb_enable_rpl_parse|maxdb_errno|maxdb_error|maxdb_escape_string|maxdb_execute|maxdb_fetch|maxdb_fetch_array|maxdb_fetch_assoc|maxdb_fetch_field|maxdb_fetch_field_direct|maxdb_fetch_fields|maxdb_fetch_lengths|maxdb_fetch_object|maxdb_fetch_row|maxdb_field_count|maxdb_field_seek|maxdb_field_tell|maxdb_free_result|maxdb_get_client_info|maxdb_get_client_version|maxdb_get_host_info|maxdb_get_metadata|maxdb_get_proto_info|maxdb_get_server_info|maxdb_get_server_version|maxdb_info|maxdb_init|maxdb_insert_id|maxdb_kill|maxdb_master_query|maxdb_more_results|maxdb_multi_query|maxdb_next_result|maxdb_num_fields|maxdb_num_rows|maxdb_options|maxdb_param_count|maxdb_ping|maxdb_prepare|maxdb_query|maxdb_real_connect|maxdb_real_escape_string|maxdb_real_query|maxdb_report|maxdb_rollback|maxdb_rpl_parse_enabled|maxdb_rpl_probe|maxdb_rpl_query_type|maxdb_select_db|maxdb_send_long_data|maxdb_send_query|maxdb_server_end|maxdb_server_init|maxdb_set_opt|maxdb_sqlstate|maxdb_ssl_set|maxdb_stat|maxdb_stmt_affected_rows|maxdb_stmt_bind_param|maxdb_stmt_bind_result|maxdb_stmt_close|maxdb_stmt_close_long_data|maxdb_stmt_data_seek|maxdb_stmt_errno|maxdb_stmt_error|maxdb_stmt_execute|maxdb_stmt_fetch|maxdb_stmt_free_result|maxdb_stmt_init|maxdb_stmt_num_rows|maxdb_stmt_param_count|maxdb_stmt_prepare|maxdb_stmt_reset|maxdb_stmt_result_metadata|maxdb_stmt_send_long_data|maxdb_stmt_sqlstate|maxdb_stmt_store_result|maxdb_store_result|maxdb_thread_id|maxdb_thread_safe|maxdb_use_result|maxdb_warning_count|mb_check_encoding|mb_convert_case|mb_convert_encoding|mb_convert_kana|mb_convert_variables|mb_decode_mimeheader|mb_decode_numericentity|mb_detect_encoding|mb_detect_order|mb_encode_mimeheader|mb_encode_numericentity|mb_encoding_aliases|mb_ereg|mb_ereg_match|mb_ereg_replace|mb_ereg_search|mb_ereg_search_getpos|mb_ereg_search_getregs|mb_ereg_search_init|mb_ereg_search_pos|mb_ereg_search_regs|mb_ereg_search_setpos|mb_eregi|mb_eregi_replace|mb_get_info|mb_http_input|mb_http_output|mb_internal_encoding|mb_language|mb_list_encodings|mb_output_handler|mb_parse_str|mb_preferred_mime_name|mb_regex_encoding|mb_regex_set_options|mb_send_mail|mb_split|mb_strcut|mb_strimwidth|mb_stripos|mb_stristr|mb_strlen|mb_strpos|mb_strrchr|mb_strrichr|mb_strripos|mb_strrpos|mb_strstr|mb_strtolower|mb_strtoupper|mb_strwidth|mb_substitute_character|mb_substr|mb_substr_count|mcrypt_cbc|mcrypt_cfb|mcrypt_create_iv|mcrypt_decrypt|mcrypt_ecb|mcrypt_enc_get_algorithms_name|mcrypt_enc_get_block_size|mcrypt_enc_get_iv_size|mcrypt_enc_get_key_size|mcrypt_enc_get_modes_name|mcrypt_enc_get_supported_key_sizes|mcrypt_enc_is_block_algorithm|mcrypt_enc_is_block_algorithm_mode|mcrypt_enc_is_block_mode|mcrypt_enc_self_test|mcrypt_encrypt|mcrypt_generic|mcrypt_generic_deinit|mcrypt_generic_end|mcrypt_generic_init|mcrypt_get_block_size|mcrypt_get_cipher_name|mcrypt_get_iv_size|mcrypt_get_key_size|mcrypt_list_algorithms|mcrypt_list_modes|mcrypt_module_close|mcrypt_module_get_algo_block_size|mcrypt_module_get_algo_key_size|mcrypt_module_get_supported_key_sizes|mcrypt_module_is_block_algorithm|mcrypt_module_is_block_algorithm_mode|mcrypt_module_is_block_mode|mcrypt_module_open|mcrypt_module_self_test|mcrypt_ofb|md5|md5_file|mdecrypt_generic|memcache|memcache_debug|memcached|memory_get_peak_usage|memory_get_usage|messageformatter|metaphone|method_exists|mhash|mhash_count|mhash_get_block_size|mhash_get_hash_name|mhash_keygen_s2k|microtime|mime_content_type|min|ming_keypress|ming_setcubicthreshold|ming_setscale|ming_setswfcompression|ming_useconstants|ming_useswfversion|mkdir|mktime|money_format|mongo|mongobindata|mongocode|mongocollection|mongoconnectionexception|mongocursor|mongocursorexception|mongocursortimeoutexception|mongodate|mongodb|mongodbref|mongoexception|mongogridfs|mongogridfscursor|mongogridfsexception|mongogridfsfile|mongoid|mongoint32|mongoint64|mongomaxkey|mongominkey|mongoregex|mongotimestamp|move_uploaded_file|mpegfile|mqseries_back|mqseries_begin|mqseries_close|mqseries_cmit|mqseries_conn|mqseries_connx|mqseries_disc|mqseries_get|mqseries_inq|mqseries_open|mqseries_put|mqseries_put1|mqseries_set|mqseries_strerror|msession_connect|msession_count|msession_create|msession_destroy|msession_disconnect|msession_find|msession_get|msession_get_array|msession_get_data|msession_inc|msession_list|msession_listvar|msession_lock|msession_plugin|msession_randstr|msession_set|msession_set_array|msession_set_data|msession_timeout|msession_uniq|msession_unlock|msg_get_queue|msg_queue_exists|msg_receive|msg_remove_queue|msg_send|msg_set_queue|msg_stat_queue|msql|msql_affected_rows|msql_close|msql_connect|msql_create_db|msql_createdb|msql_data_seek|msql_db_query|msql_dbname|msql_drop_db|msql_error|msql_fetch_array|msql_fetch_field|msql_fetch_object|msql_fetch_row|msql_field_flags|msql_field_len|msql_field_name|msql_field_seek|msql_field_table|msql_field_type|msql_fieldflags|msql_fieldlen|msql_fieldname|msql_fieldtable|msql_fieldtype|msql_free_result|msql_list_dbs|msql_list_fields|msql_list_tables|msql_num_fields|msql_num_rows|msql_numfields|msql_numrows|msql_pconnect|msql_query|msql_regcase|msql_result|msql_select_db|msql_tablename|mssql_bind|mssql_close|mssql_connect|mssql_data_seek|mssql_execute|mssql_fetch_array|mssql_fetch_assoc|mssql_fetch_batch|mssql_fetch_field|mssql_fetch_object|mssql_fetch_row|mssql_field_length|mssql_field_name|mssql_field_seek|mssql_field_type|mssql_free_result|mssql_free_statement|mssql_get_last_message|mssql_guid_string|mssql_init|mssql_min_error_severity|mssql_min_message_severity|mssql_next_result|mssql_num_fields|mssql_num_rows|mssql_pconnect|mssql_query|mssql_result|mssql_rows_affected|mssql_select_db|mt_getrandmax|mt_rand|mt_srand|multipleiterator|mysql_affected_rows|mysql_client_encoding|mysql_close|mysql_connect|mysql_create_db|mysql_data_seek|mysql_db_name|mysql_db_query|mysql_drop_db|mysql_errno|mysql_error|mysql_escape_string|mysql_fetch_array|mysql_fetch_assoc|mysql_fetch_field|mysql_fetch_lengths|mysql_fetch_object|mysql_fetch_row|mysql_field_flags|mysql_field_len|mysql_field_name|mysql_field_seek|mysql_field_table|mysql_field_type|mysql_free_result|mysql_get_client_info|mysql_get_host_info|mysql_get_proto_info|mysql_get_server_info|mysql_info|mysql_insert_id|mysql_list_dbs|mysql_list_fields|mysql_list_processes|mysql_list_tables|mysql_num_fields|mysql_num_rows|mysql_pconnect|mysql_ping|mysql_query|mysql_real_escape_string|mysql_result|mysql_select_db|mysql_set_charset|mysql_stat|mysql_tablename|mysql_thread_id|mysql_unbuffered_query|mysqli|mysqli_affected_rows|mysqli_autocommit|mysqli_bind_param|mysqli_bind_result|mysqli_cache_stats|mysqli_change_user|mysqli_character_set_name|mysqli_client_encoding|mysqli_close|mysqli_commit|mysqli_connect|mysqli_connect_errno|mysqli_connect_error|mysqli_data_seek|mysqli_debug|mysqli_disable_reads_from_master|mysqli_disable_rpl_parse|mysqli_driver|mysqli_dump_debug_info|mysqli_embedded_server_end|mysqli_embedded_server_start|mysqli_enable_reads_from_master|mysqli_enable_rpl_parse|mysqli_errno|mysqli_error|mysqli_escape_string|mysqli_execute|mysqli_fetch|mysqli_fetch_all|mysqli_fetch_array|mysqli_fetch_assoc|mysqli_fetch_field|mysqli_fetch_field_direct|mysqli_fetch_fields|mysqli_fetch_lengths|mysqli_fetch_object|mysqli_fetch_row|mysqli_field_count|mysqli_field_seek|mysqli_field_tell|mysqli_free_result|mysqli_get_charset|mysqli_get_client_info|mysqli_get_client_stats|mysqli_get_client_version|mysqli_get_connection_stats|mysqli_get_host_info|mysqli_get_metadata|mysqli_get_proto_info|mysqli_get_server_info|mysqli_get_server_version|mysqli_get_warnings|mysqli_info|mysqli_init|mysqli_insert_id|mysqli_kill|mysqli_link_construct|mysqli_master_query|mysqli_more_results|mysqli_multi_query|mysqli_next_result|mysqli_num_fields|mysqli_num_rows|mysqli_options|mysqli_param_count|mysqli_ping|mysqli_poll|mysqli_prepare|mysqli_query|mysqli_real_connect|mysqli_real_escape_string|mysqli_real_query|mysqli_reap_async_query|mysqli_refresh|mysqli_report|mysqli_result|mysqli_rollback|mysqli_rpl_parse_enabled|mysqli_rpl_probe|mysqli_rpl_query_type|mysqli_select_db|mysqli_send_long_data|mysqli_send_query|mysqli_set_charset|mysqli_set_local_infile_default|mysqli_set_local_infile_handler|mysqli_set_opt|mysqli_slave_query|mysqli_sqlstate|mysqli_ssl_set|mysqli_stat|mysqli_stmt|mysqli_stmt_affected_rows|mysqli_stmt_attr_get|mysqli_stmt_attr_set|mysqli_stmt_bind_param|mysqli_stmt_bind_result|mysqli_stmt_close|mysqli_stmt_data_seek|mysqli_stmt_errno|mysqli_stmt_error|mysqli_stmt_execute|mysqli_stmt_fetch|mysqli_stmt_field_count|mysqli_stmt_free_result|mysqli_stmt_get_result|mysqli_stmt_get_warnings|mysqli_stmt_init|mysqli_stmt_insert_id|mysqli_stmt_next_result|mysqli_stmt_num_rows|mysqli_stmt_param_count|mysqli_stmt_prepare|mysqli_stmt_reset|mysqli_stmt_result_metadata|mysqli_stmt_send_long_data|mysqli_stmt_sqlstate|mysqli_stmt_store_result|mysqli_store_result|mysqli_thread_id|mysqli_thread_safe|mysqli_use_result|mysqli_warning|mysqli_warning_count|mysqlnd_ms_get_stats|mysqlnd_ms_query_is_select|mysqlnd_ms_set_user_pick_server|mysqlnd_qc_change_handler|mysqlnd_qc_clear_cache|mysqlnd_qc_get_cache_info|mysqlnd_qc_get_core_stats|mysqlnd_qc_get_handler|mysqlnd_qc_get_query_trace_log|mysqlnd_qc_set_user_handlers|natcasesort|natsort|ncurses_addch|ncurses_addchnstr|ncurses_addchstr|ncurses_addnstr|ncurses_addstr|ncurses_assume_default_colors|ncurses_attroff|ncurses_attron|ncurses_attrset|ncurses_baudrate|ncurses_beep|ncurses_bkgd|ncurses_bkgdset|ncurses_border|ncurses_bottom_panel|ncurses_can_change_color|ncurses_cbreak|ncurses_clear|ncurses_clrtobot|ncurses_clrtoeol|ncurses_color_content|ncurses_color_set|ncurses_curs_set|ncurses_def_prog_mode|ncurses_def_shell_mode|ncurses_define_key|ncurses_del_panel|ncurses_delay_output|ncurses_delch|ncurses_deleteln|ncurses_delwin|ncurses_doupdate|ncurses_echo|ncurses_echochar|ncurses_end|ncurses_erase|ncurses_erasechar|ncurses_filter|ncurses_flash|ncurses_flushinp|ncurses_getch|ncurses_getmaxyx|ncurses_getmouse|ncurses_getyx|ncurses_halfdelay|ncurses_has_colors|ncurses_has_ic|ncurses_has_il|ncurses_has_key|ncurses_hide_panel|ncurses_hline|ncurses_inch|ncurses_init|ncurses_init_color|ncurses_init_pair|ncurses_insch|ncurses_insdelln|ncurses_insertln|ncurses_insstr|ncurses_instr|ncurses_isendwin|ncurses_keyok|ncurses_keypad|ncurses_killchar|ncurses_longname|ncurses_meta|ncurses_mouse_trafo|ncurses_mouseinterval|ncurses_mousemask|ncurses_move|ncurses_move_panel|ncurses_mvaddch|ncurses_mvaddchnstr|ncurses_mvaddchstr|ncurses_mvaddnstr|ncurses_mvaddstr|ncurses_mvcur|ncurses_mvdelch|ncurses_mvgetch|ncurses_mvhline|ncurses_mvinch|ncurses_mvvline|ncurses_mvwaddstr|ncurses_napms|ncurses_new_panel|ncurses_newpad|ncurses_newwin|ncurses_nl|ncurses_nocbreak|ncurses_noecho|ncurses_nonl|ncurses_noqiflush|ncurses_noraw|ncurses_pair_content|ncurses_panel_above|ncurses_panel_below|ncurses_panel_window|ncurses_pnoutrefresh|ncurses_prefresh|ncurses_putp|ncurses_qiflush|ncurses_raw|ncurses_refresh|ncurses_replace_panel|ncurses_reset_prog_mode|ncurses_reset_shell_mode|ncurses_resetty|ncurses_savetty|ncurses_scr_dump|ncurses_scr_init|ncurses_scr_restore|ncurses_scr_set|ncurses_scrl|ncurses_show_panel|ncurses_slk_attr|ncurses_slk_attroff|ncurses_slk_attron|ncurses_slk_attrset|ncurses_slk_clear|ncurses_slk_color|ncurses_slk_init|ncurses_slk_noutrefresh|ncurses_slk_refresh|ncurses_slk_restore|ncurses_slk_set|ncurses_slk_touch|ncurses_standend|ncurses_standout|ncurses_start_color|ncurses_termattrs|ncurses_termname|ncurses_timeout|ncurses_top_panel|ncurses_typeahead|ncurses_ungetch|ncurses_ungetmouse|ncurses_update_panels|ncurses_use_default_colors|ncurses_use_env|ncurses_use_extended_names|ncurses_vidattr|ncurses_vline|ncurses_waddch|ncurses_waddstr|ncurses_wattroff|ncurses_wattron|ncurses_wattrset|ncurses_wborder|ncurses_wclear|ncurses_wcolor_set|ncurses_werase|ncurses_wgetch|ncurses_whline|ncurses_wmouse_trafo|ncurses_wmove|ncurses_wnoutrefresh|ncurses_wrefresh|ncurses_wstandend|ncurses_wstandout|ncurses_wvline|newinstance|newinstanceargs|newt_bell|newt_button|newt_button_bar|newt_centered_window|newt_checkbox|newt_checkbox_get_value|newt_checkbox_set_flags|newt_checkbox_set_value|newt_checkbox_tree|newt_checkbox_tree_add_item|newt_checkbox_tree_find_item|newt_checkbox_tree_get_current|newt_checkbox_tree_get_entry_value|newt_checkbox_tree_get_multi_selection|newt_checkbox_tree_get_selection|newt_checkbox_tree_multi|newt_checkbox_tree_set_current|newt_checkbox_tree_set_entry|newt_checkbox_tree_set_entry_value|newt_checkbox_tree_set_width|newt_clear_key_buffer|newt_cls|newt_compact_button|newt_component_add_callback|newt_component_takes_focus|newt_create_grid|newt_cursor_off|newt_cursor_on|newt_delay|newt_draw_form|newt_draw_root_text|newt_entry|newt_entry_get_value|newt_entry_set|newt_entry_set_filter|newt_entry_set_flags|newt_finished|newt_form|newt_form_add_component|newt_form_add_components|newt_form_add_hot_key|newt_form_destroy|newt_form_get_current|newt_form_run|newt_form_set_background|newt_form_set_height|newt_form_set_size|newt_form_set_timer|newt_form_set_width|newt_form_watch_fd|newt_get_screen_size|newt_grid_add_components_to_form|newt_grid_basic_window|newt_grid_free|newt_grid_get_size|newt_grid_h_close_stacked|newt_grid_h_stacked|newt_grid_place|newt_grid_set_field|newt_grid_simple_window|newt_grid_v_close_stacked|newt_grid_v_stacked|newt_grid_wrapped_window|newt_grid_wrapped_window_at|newt_init|newt_label|newt_label_set_text|newt_listbox|newt_listbox_append_entry|newt_listbox_clear|newt_listbox_clear_selection|newt_listbox_delete_entry|newt_listbox_get_current|newt_listbox_get_selection|newt_listbox_insert_entry|newt_listbox_item_count|newt_listbox_select_item|newt_listbox_set_current|newt_listbox_set_current_by_key|newt_listbox_set_data|newt_listbox_set_entry|newt_listbox_set_width|newt_listitem|newt_listitem_get_data|newt_listitem_set|newt_open_window|newt_pop_help_line|newt_pop_window|newt_push_help_line|newt_radio_get_current|newt_radiobutton|newt_redraw_help_line|newt_reflow_text|newt_refresh|newt_resize_screen|newt_resume|newt_run_form|newt_scale|newt_scale_set|newt_scrollbar_set|newt_set_help_callback|newt_set_suspend_callback|newt_suspend|newt_textbox|newt_textbox_get_num_lines|newt_textbox_reflowed|newt_textbox_set_height|newt_textbox_set_text|newt_vertical_scrollbar|newt_wait_for_key|newt_win_choice|newt_win_entries|newt_win_menu|newt_win_message|newt_win_messagev|newt_win_ternary|next|ngettext|nl2br|nl_langinfo|norewinditerator|normalizer|notes_body|notes_copy_db|notes_create_db|notes_create_note|notes_drop_db|notes_find_note|notes_header_info|notes_list_msgs|notes_mark_read|notes_mark_unread|notes_nav_create|notes_search|notes_unread|notes_version|nsapi_request_headers|nsapi_response_headers|nsapi_virtual|nthmac|number_format|numberformatter|oauth|oauth_get_sbs|oauth_urlencode|oauthexception|oauthprovider|ob_clean|ob_deflatehandler|ob_end_clean|ob_end_flush|ob_etaghandler|ob_flush|ob_get_clean|ob_get_contents|ob_get_flush|ob_get_length|ob_get_level|ob_get_status|ob_gzhandler|ob_iconv_handler|ob_implicit_flush|ob_inflatehandler|ob_list_handlers|ob_start|ob_tidyhandler|oci_bind_array_by_name|oci_bind_by_name|oci_cancel|oci_client_version|oci_close|oci_collection_append|oci_collection_assign|oci_collection_element_assign|oci_collection_element_get|oci_collection_free|oci_collection_max|oci_collection_size|oci_collection_trim|oci_commit|oci_connect|oci_define_by_name|oci_error|oci_execute|oci_fetch|oci_fetch_all|oci_fetch_array|oci_fetch_assoc|oci_fetch_object|oci_fetch_row|oci_field_is_null|oci_field_name|oci_field_precision|oci_field_scale|oci_field_size|oci_field_type|oci_field_type_raw|oci_free_statement|oci_internal_debug|oci_lob_append|oci_lob_close|oci_lob_copy|oci_lob_eof|oci_lob_erase|oci_lob_export|oci_lob_flush|oci_lob_free|oci_lob_getbuffering|oci_lob_import|oci_lob_is_equal|oci_lob_load|oci_lob_read|oci_lob_rewind|oci_lob_save|oci_lob_savefile|oci_lob_seek|oci_lob_setbuffering|oci_lob_size|oci_lob_tell|oci_lob_truncate|oci_lob_write|oci_lob_writetemporary|oci_lob_writetofile|oci_new_collection|oci_new_connect|oci_new_cursor|oci_new_descriptor|oci_num_fields|oci_num_rows|oci_parse|oci_password_change|oci_pconnect|oci_result|oci_rollback|oci_server_version|oci_set_action|oci_set_client_identifier|oci_set_client_info|oci_set_edition|oci_set_module_name|oci_set_prefetch|oci_statement_type|ocibindbyname|ocicancel|ocicloselob|ocicollappend|ocicollassign|ocicollassignelem|ocicollgetelem|ocicollmax|ocicollsize|ocicolltrim|ocicolumnisnull|ocicolumnname|ocicolumnprecision|ocicolumnscale|ocicolumnsize|ocicolumntype|ocicolumntyperaw|ocicommit|ocidefinebyname|ocierror|ociexecute|ocifetch|ocifetchinto|ocifetchstatement|ocifreecollection|ocifreecursor|ocifreedesc|ocifreestatement|ociinternaldebug|ociloadlob|ocilogoff|ocilogon|ocinewcollection|ocinewcursor|ocinewdescriptor|ocinlogon|ocinumcols|ociparse|ociplogon|ociresult|ocirollback|ocirowcount|ocisavelob|ocisavelobfile|ociserverversion|ocisetprefetch|ocistatementtype|ociwritelobtofile|ociwritetemporarylob|octdec|odbc_autocommit|odbc_binmode|odbc_close|odbc_close_all|odbc_columnprivileges|odbc_columns|odbc_commit|odbc_connect|odbc_cursor|odbc_data_source|odbc_do|odbc_error|odbc_errormsg|odbc_exec|odbc_execute|odbc_fetch_array|odbc_fetch_into|odbc_fetch_object|odbc_fetch_row|odbc_field_len|odbc_field_name|odbc_field_num|odbc_field_precision|odbc_field_scale|odbc_field_type|odbc_foreignkeys|odbc_free_result|odbc_gettypeinfo|odbc_longreadlen|odbc_next_result|odbc_num_fields|odbc_num_rows|odbc_pconnect|odbc_prepare|odbc_primarykeys|odbc_procedurecolumns|odbc_procedures|odbc_result|odbc_result_all|odbc_rollback|odbc_setoption|odbc_specialcolumns|odbc_statistics|odbc_tableprivileges|odbc_tables|openal_buffer_create|openal_buffer_data|openal_buffer_destroy|openal_buffer_get|openal_buffer_loadwav|openal_context_create|openal_context_current|openal_context_destroy|openal_context_process|openal_context_suspend|openal_device_close|openal_device_open|openal_listener_get|openal_listener_set|openal_source_create|openal_source_destroy|openal_source_get|openal_source_pause|openal_source_play|openal_source_rewind|openal_source_set|openal_source_stop|openal_stream|opendir|openlog|openssl_cipher_iv_length|openssl_csr_export|openssl_csr_export_to_file|openssl_csr_get_public_key|openssl_csr_get_subject|openssl_csr_new|openssl_csr_sign|openssl_decrypt|openssl_dh_compute_key|openssl_digest|openssl_encrypt|openssl_error_string|openssl_free_key|openssl_get_cipher_methods|openssl_get_md_methods|openssl_get_privatekey|openssl_get_publickey|openssl_open|openssl_pkcs12_export|openssl_pkcs12_export_to_file|openssl_pkcs12_read|openssl_pkcs7_decrypt|openssl_pkcs7_encrypt|openssl_pkcs7_sign|openssl_pkcs7_verify|openssl_pkey_export|openssl_pkey_export_to_file|openssl_pkey_free|openssl_pkey_get_details|openssl_pkey_get_private|openssl_pkey_get_public|openssl_pkey_new|openssl_private_decrypt|openssl_private_encrypt|openssl_public_decrypt|openssl_public_encrypt|openssl_random_pseudo_bytes|openssl_seal|openssl_sign|openssl_verify|openssl_x509_check_private_key|openssl_x509_checkpurpose|openssl_x509_export|openssl_x509_export_to_file|openssl_x509_free|openssl_x509_parse|openssl_x509_read|ord|outeriterator|outofboundsexception|outofrangeexception|output_add_rewrite_var|output_reset_rewrite_vars|overflowexception|overload|override_function|ovrimos_close|ovrimos_commit|ovrimos_connect|ovrimos_cursor|ovrimos_exec|ovrimos_execute|ovrimos_fetch_into|ovrimos_fetch_row|ovrimos_field_len|ovrimos_field_name|ovrimos_field_num|ovrimos_field_type|ovrimos_free_result|ovrimos_longreadlen|ovrimos_num_fields|ovrimos_num_rows|ovrimos_prepare|ovrimos_result|ovrimos_result_all|ovrimos_rollback|pack|parentiterator|parse_ini_file|parse_ini_string|parse_str|parse_url|parsekit_compile_file|parsekit_compile_string|parsekit_func_arginfo|passthru|pathinfo|pclose|pcntl_alarm|pcntl_exec|pcntl_fork|pcntl_getpriority|pcntl_setpriority|pcntl_signal|pcntl_signal_dispatch|pcntl_sigprocmask|pcntl_sigtimedwait|pcntl_sigwaitinfo|pcntl_wait|pcntl_waitpid|pcntl_wexitstatus|pcntl_wifexited|pcntl_wifsignaled|pcntl_wifstopped|pcntl_wstopsig|pcntl_wtermsig|pdf_activate_item|pdf_add_annotation|pdf_add_bookmark|pdf_add_launchlink|pdf_add_locallink|pdf_add_nameddest|pdf_add_note|pdf_add_outline|pdf_add_pdflink|pdf_add_table_cell|pdf_add_textflow|pdf_add_thumbnail|pdf_add_weblink|pdf_arc|pdf_arcn|pdf_attach_file|pdf_begin_document|pdf_begin_font|pdf_begin_glyph|pdf_begin_item|pdf_begin_layer|pdf_begin_page|pdf_begin_page_ext|pdf_begin_pattern|pdf_begin_template|pdf_begin_template_ext|pdf_circle|pdf_clip|pdf_close|pdf_close_image|pdf_close_pdi|pdf_close_pdi_page|pdf_closepath|pdf_closepath_fill_stroke|pdf_closepath_stroke|pdf_concat|pdf_continue_text|pdf_create_3dview|pdf_create_action|pdf_create_annotation|pdf_create_bookmark|pdf_create_field|pdf_create_fieldgroup|pdf_create_gstate|pdf_create_pvf|pdf_create_textflow|pdf_curveto|pdf_define_layer|pdf_delete|pdf_delete_pvf|pdf_delete_table|pdf_delete_textflow|pdf_encoding_set_char|pdf_end_document|pdf_end_font|pdf_end_glyph|pdf_end_item|pdf_end_layer|pdf_end_page|pdf_end_page_ext|pdf_end_pattern|pdf_end_template|pdf_endpath|pdf_fill|pdf_fill_imageblock|pdf_fill_pdfblock|pdf_fill_stroke|pdf_fill_textblock|pdf_findfont|pdf_fit_image|pdf_fit_pdi_page|pdf_fit_table|pdf_fit_textflow|pdf_fit_textline|pdf_get_apiname|pdf_get_buffer|pdf_get_errmsg|pdf_get_errnum|pdf_get_font|pdf_get_fontname|pdf_get_fontsize|pdf_get_image_height|pdf_get_image_width|pdf_get_majorversion|pdf_get_minorversion|pdf_get_parameter|pdf_get_pdi_parameter|pdf_get_pdi_value|pdf_get_value|pdf_info_font|pdf_info_matchbox|pdf_info_table|pdf_info_textflow|pdf_info_textline|pdf_initgraphics|pdf_lineto|pdf_load_3ddata|pdf_load_font|pdf_load_iccprofile|pdf_load_image|pdf_makespotcolor|pdf_moveto|pdf_new|pdf_open_ccitt|pdf_open_file|pdf_open_gif|pdf_open_image|pdf_open_image_file|pdf_open_jpeg|pdf_open_memory_image|pdf_open_pdi|pdf_open_pdi_document|pdf_open_pdi_page|pdf_open_tiff|pdf_pcos_get_number|pdf_pcos_get_stream|pdf_pcos_get_string|pdf_place_image|pdf_place_pdi_page|pdf_process_pdi|pdf_rect|pdf_restore|pdf_resume_page|pdf_rotate|pdf_save|pdf_scale|pdf_set_border_color|pdf_set_border_dash|pdf_set_border_style|pdf_set_char_spacing|pdf_set_duration|pdf_set_gstate|pdf_set_horiz_scaling|pdf_set_info|pdf_set_info_author|pdf_set_info_creator|pdf_set_info_keywords|pdf_set_info_subject|pdf_set_info_title|pdf_set_layer_dependency|pdf_set_leading|pdf_set_parameter|pdf_set_text_matrix|pdf_set_text_pos|pdf_set_text_rendering|pdf_set_text_rise|pdf_set_value|pdf_set_word_spacing|pdf_setcolor|pdf_setdash|pdf_setdashpattern|pdf_setflat|pdf_setfont|pdf_setgray|pdf_setgray_fill|pdf_setgray_stroke|pdf_setlinecap|pdf_setlinejoin|pdf_setlinewidth|pdf_setmatrix|pdf_setmiterlimit|pdf_setpolydash|pdf_setrgbcolor|pdf_setrgbcolor_fill|pdf_setrgbcolor_stroke|pdf_shading|pdf_shading_pattern|pdf_shfill|pdf_show|pdf_show_boxed|pdf_show_xy|pdf_skew|pdf_stringwidth|pdf_stroke|pdf_suspend_page|pdf_translate|pdf_utf16_to_utf8|pdf_utf32_to_utf16|pdf_utf8_to_utf16|pdo|pdo_cubrid_schema|pdo_pgsqllobcreate|pdo_pgsqllobopen|pdo_pgsqllobunlink|pdo_sqlitecreateaggregate|pdo_sqlitecreatefunction|pdoexception|pdostatement|pfsockopen|pg_affected_rows|pg_cancel_query|pg_client_encoding|pg_close|pg_connect|pg_connection_busy|pg_connection_reset|pg_connection_status|pg_convert|pg_copy_from|pg_copy_to|pg_dbname|pg_delete|pg_end_copy|pg_escape_bytea|pg_escape_string|pg_execute|pg_fetch_all|pg_fetch_all_columns|pg_fetch_array|pg_fetch_assoc|pg_fetch_object|pg_fetch_result|pg_fetch_row|pg_field_is_null|pg_field_name|pg_field_num|pg_field_prtlen|pg_field_size|pg_field_table|pg_field_type|pg_field_type_oid|pg_free_result|pg_get_notify|pg_get_pid|pg_get_result|pg_host|pg_insert|pg_last_error|pg_last_notice|pg_last_oid|pg_lo_close|pg_lo_create|pg_lo_export|pg_lo_import|pg_lo_open|pg_lo_read|pg_lo_read_all|pg_lo_seek|pg_lo_tell|pg_lo_unlink|pg_lo_write|pg_meta_data|pg_num_fields|pg_num_rows|pg_options|pg_parameter_status|pg_pconnect|pg_ping|pg_port|pg_prepare|pg_put_line|pg_query|pg_query_params|pg_result_error|pg_result_error_field|pg_result_seek|pg_result_status|pg_select|pg_send_execute|pg_send_prepare|pg_send_query|pg_send_query_params|pg_set_client_encoding|pg_set_error_verbosity|pg_trace|pg_transaction_status|pg_tty|pg_unescape_bytea|pg_untrace|pg_update|pg_version|php_check_syntax|php_ini_loaded_file|php_ini_scanned_files|php_logo_guid|php_sapi_name|php_strip_whitespace|php_uname|phpcredits|phpinfo|phpversion|pi|png2wbmp|popen|pos|posix_access|posix_ctermid|posix_errno|posix_get_last_error|posix_getcwd|posix_getegid|posix_geteuid|posix_getgid|posix_getgrgid|posix_getgrnam|posix_getgroups|posix_getlogin|posix_getpgid|posix_getpgrp|posix_getpid|posix_getppid|posix_getpwnam|posix_getpwuid|posix_getrlimit|posix_getsid|posix_getuid|posix_initgroups|posix_isatty|posix_kill|posix_mkfifo|posix_mknod|posix_setegid|posix_seteuid|posix_setgid|posix_setpgid|posix_setsid|posix_setuid|posix_strerror|posix_times|posix_ttyname|posix_uname|pow|preg_filter|preg_grep|preg_last_error|preg_match|preg_match_all|preg_quote|preg_replace|preg_replace_callback|preg_split|prev|print|print_r|printer_abort|printer_close|printer_create_brush|printer_create_dc|printer_create_font|printer_create_pen|printer_delete_brush|printer_delete_dc|printer_delete_font|printer_delete_pen|printer_draw_bmp|printer_draw_chord|printer_draw_elipse|printer_draw_line|printer_draw_pie|printer_draw_rectangle|printer_draw_roundrect|printer_draw_text|printer_end_doc|printer_end_page|printer_get_option|printer_list|printer_logical_fontheight|printer_open|printer_select_brush|printer_select_font|printer_select_pen|printer_set_option|printer_start_doc|printer_start_page|printer_write|printf|proc_close|proc_get_status|proc_nice|proc_open|proc_terminate|property_exists|ps_add_bookmark|ps_add_launchlink|ps_add_locallink|ps_add_note|ps_add_pdflink|ps_add_weblink|ps_arc|ps_arcn|ps_begin_page|ps_begin_pattern|ps_begin_template|ps_circle|ps_clip|ps_close|ps_close_image|ps_closepath|ps_closepath_stroke|ps_continue_text|ps_curveto|ps_delete|ps_end_page|ps_end_pattern|ps_end_template|ps_fill|ps_fill_stroke|ps_findfont|ps_get_buffer|ps_get_parameter|ps_get_value|ps_hyphenate|ps_include_file|ps_lineto|ps_makespotcolor|ps_moveto|ps_new|ps_open_file|ps_open_image|ps_open_image_file|ps_open_memory_image|ps_place_image|ps_rect|ps_restore|ps_rotate|ps_save|ps_scale|ps_set_border_color|ps_set_border_dash|ps_set_border_style|ps_set_info|ps_set_parameter|ps_set_text_pos|ps_set_value|ps_setcolor|ps_setdash|ps_setflat|ps_setfont|ps_setgray|ps_setlinecap|ps_setlinejoin|ps_setlinewidth|ps_setmiterlimit|ps_setoverprintmode|ps_setpolydash|ps_shading|ps_shading_pattern|ps_shfill|ps_show|ps_show2|ps_show_boxed|ps_show_xy|ps_show_xy2|ps_string_geometry|ps_stringwidth|ps_stroke|ps_symbol|ps_symbol_name|ps_symbol_width|ps_translate|pspell_add_to_personal|pspell_add_to_session|pspell_check|pspell_clear_session|pspell_config_create|pspell_config_data_dir|pspell_config_dict_dir|pspell_config_ignore|pspell_config_mode|pspell_config_personal|pspell_config_repl|pspell_config_runtogether|pspell_config_save_repl|pspell_new|pspell_new_config|pspell_new_personal|pspell_save_wordlist|pspell_store_replacement|pspell_suggest|putenv|px_close|px_create_fp|px_date2string|px_delete|px_delete_record|px_get_field|px_get_info|px_get_parameter|px_get_record|px_get_schema|px_get_value|px_insert_record|px_new|px_numfields|px_numrecords|px_open_fp|px_put_record|px_retrieve_record|px_set_blob_file|px_set_parameter|px_set_tablename|px_set_targetencoding|px_set_value|px_timestamp2string|px_update_record|qdom_error|qdom_tree|quoted_printable_decode|quoted_printable_encode|quotemeta|rad2deg|radius_acct_open|radius_add_server|radius_auth_open|radius_close|radius_config|radius_create_request|radius_cvt_addr|radius_cvt_int|radius_cvt_string|radius_demangle|radius_demangle_mppe_key|radius_get_attr|radius_get_vendor_attr|radius_put_addr|radius_put_attr|radius_put_int|radius_put_string|radius_put_vendor_addr|radius_put_vendor_attr|radius_put_vendor_int|radius_put_vendor_string|radius_request_authenticator|radius_send_request|radius_server_secret|radius_strerror|rand|range|rangeexception|rar_wrapper_cache_stats|rararchive|rarentry|rarexception|rawurldecode|rawurlencode|read_exif_data|readdir|readfile|readgzfile|readline|readline_add_history|readline_callback_handler_install|readline_callback_handler_remove|readline_callback_read_char|readline_clear_history|readline_completion_function|readline_info|readline_list_history|readline_on_new_line|readline_read_history|readline_redisplay|readline_write_history|readlink|realpath|realpath_cache_get|realpath_cache_size|recode|recode_file|recode_string|recursivearrayiterator|recursivecachingiterator|recursivecallbackfilteriterator|recursivedirectoryiterator|recursivefilteriterator|recursiveiterator|recursiveiteratoriterator|recursiveregexiterator|recursivetreeiterator|reflection|reflectionclass|reflectionexception|reflectionextension|reflectionfunction|reflectionfunctionabstract|reflectionmethod|reflectionobject|reflectionparameter|reflectionproperty|reflector|regexiterator|register_shutdown_function|register_tick_function|rename|rename_function|require|require_once|reset|resetValue|resourcebundle|restore_error_handler|restore_exception_handler|restore_include_path|return|rewind|rewinddir|rmdir|round|rpm_close|rpm_get_tag|rpm_is_valid|rpm_open|rpm_version|rrd_create|rrd_error|rrd_fetch|rrd_first|rrd_graph|rrd_info|rrd_last|rrd_lastupdate|rrd_restore|rrd_tune|rrd_update|rrd_xport|rrdcreator|rrdgraph|rrdupdater|rsort|rtrim|runkit_class_adopt|runkit_class_emancipate|runkit_constant_add|runkit_constant_redefine|runkit_constant_remove|runkit_function_add|runkit_function_copy|runkit_function_redefine|runkit_function_remove|runkit_function_rename|runkit_import|runkit_lint|runkit_lint_file|runkit_method_add|runkit_method_copy|runkit_method_redefine|runkit_method_remove|runkit_method_rename|runkit_return_value_used|runkit_sandbox_output_handler|runkit_superglobals|runtimeexception|samconnection_commit|samconnection_connect|samconnection_constructor|samconnection_disconnect|samconnection_errno|samconnection_error|samconnection_isconnected|samconnection_peek|samconnection_peekall|samconnection_receive|samconnection_remove|samconnection_rollback|samconnection_send|samconnection_setDebug|samconnection_subscribe|samconnection_unsubscribe|sammessage_body|sammessage_constructor|sammessage_header|sca_createdataobject|sca_getservice|sca_localproxy_createdataobject|sca_soapproxy_createdataobject|scandir|sdo_das_changesummary_beginlogging|sdo_das_changesummary_endlogging|sdo_das_changesummary_getchangeddataobjects|sdo_das_changesummary_getchangetype|sdo_das_changesummary_getoldcontainer|sdo_das_changesummary_getoldvalues|sdo_das_changesummary_islogging|sdo_das_datafactory_addpropertytotype|sdo_das_datafactory_addtype|sdo_das_datafactory_getdatafactory|sdo_das_dataobject_getchangesummary|sdo_das_relational_applychanges|sdo_das_relational_construct|sdo_das_relational_createrootdataobject|sdo_das_relational_executepreparedquery|sdo_das_relational_executequery|sdo_das_setting_getlistindex|sdo_das_setting_getpropertyindex|sdo_das_setting_getpropertyname|sdo_das_setting_getvalue|sdo_das_setting_isset|sdo_das_xml_addtypes|sdo_das_xml_create|sdo_das_xml_createdataobject|sdo_das_xml_createdocument|sdo_das_xml_document_getrootdataobject|sdo_das_xml_document_getrootelementname|sdo_das_xml_document_getrootelementuri|sdo_das_xml_document_setencoding|sdo_das_xml_document_setxmldeclaration|sdo_das_xml_document_setxmlversion|sdo_das_xml_loadfile|sdo_das_xml_loadstring|sdo_das_xml_savefile|sdo_das_xml_savestring|sdo_datafactory_create|sdo_dataobject_clear|sdo_dataobject_createdataobject|sdo_dataobject_getcontainer|sdo_dataobject_getsequence|sdo_dataobject_gettypename|sdo_dataobject_gettypenamespaceuri|sdo_exception_getcause|sdo_list_insert|sdo_model_property_getcontainingtype|sdo_model_property_getdefault|sdo_model_property_getname|sdo_model_property_gettype|sdo_model_property_iscontainment|sdo_model_property_ismany|sdo_model_reflectiondataobject_construct|sdo_model_reflectiondataobject_export|sdo_model_reflectiondataobject_getcontainmentproperty|sdo_model_reflectiondataobject_getinstanceproperties|sdo_model_reflectiondataobject_gettype|sdo_model_type_getbasetype|sdo_model_type_getname|sdo_model_type_getnamespaceuri|sdo_model_type_getproperties|sdo_model_type_getproperty|sdo_model_type_isabstracttype|sdo_model_type_isdatatype|sdo_model_type_isinstance|sdo_model_type_isopentype|sdo_model_type_issequencedtype|sdo_sequence_getproperty|sdo_sequence_insert|sdo_sequence_move|seekableiterator|sem_acquire|sem_get|sem_release|sem_remove|serializable|serialize|session_cache_expire|session_cache_limiter|session_commit|session_decode|session_destroy|session_encode|session_get_cookie_params|session_id|session_is_registered|session_module_name|session_name|session_pgsql_add_error|session_pgsql_get_error|session_pgsql_get_field|session_pgsql_reset|session_pgsql_set_field|session_pgsql_status|session_regenerate_id|session_register|session_save_path|session_set_cookie_params|session_set_save_handler|session_start|session_unregister|session_unset|session_write_close|setCounterClass|set_error_handler|set_exception_handler|set_file_buffer|set_include_path|set_magic_quotes_runtime|set_socket_blocking|set_time_limit|setcookie|setlocale|setproctitle|setrawcookie|setstaticpropertyvalue|setthreadtitle|settype|sha1|sha1_file|shell_exec|shm_attach|shm_detach|shm_get_var|shm_has_var|shm_put_var|shm_remove|shm_remove_var|shmop_close|shmop_delete|shmop_open|shmop_read|shmop_size|shmop_write|show_source|shuffle|signeurlpaiement|similar_text|simplexml_import_dom|simplexml_load_file|simplexml_load_string|simplexmlelement|simplexmliterator|sin|sinh|sizeof|sleep|snmp|snmp2_get|snmp2_getnext|snmp2_real_walk|snmp2_set|snmp2_walk|snmp3_get|snmp3_getnext|snmp3_real_walk|snmp3_set|snmp3_walk|snmp_get_quick_print|snmp_get_valueretrieval|snmp_read_mib|snmp_set_enum_print|snmp_set_oid_numeric_print|snmp_set_oid_output_format|snmp_set_quick_print|snmp_set_valueretrieval|snmpget|snmpgetnext|snmprealwalk|snmpset|snmpwalk|snmpwalkoid|soapclient|soapfault|soapheader|soapparam|soapserver|soapvar|socket_accept|socket_bind|socket_clear_error|socket_close|socket_connect|socket_create|socket_create_listen|socket_create_pair|socket_get_option|socket_get_status|socket_getpeername|socket_getsockname|socket_last_error|socket_listen|socket_read|socket_recv|socket_recvfrom|socket_select|socket_send|socket_sendto|socket_set_block|socket_set_blocking|socket_set_nonblock|socket_set_option|socket_set_timeout|socket_shutdown|socket_strerror|socket_write|solr_get_version|solrclient|solrclientexception|solrdocument|solrdocumentfield|solrexception|solrgenericresponse|solrillegalargumentexception|solrillegaloperationexception|solrinputdocument|solrmodifiableparams|solrobject|solrparams|solrpingresponse|solrquery|solrqueryresponse|solrresponse|solrupdateresponse|solrutils|sort|soundex|sphinxclient|spl_autoload|spl_autoload_call|spl_autoload_extensions|spl_autoload_functions|spl_autoload_register|spl_autoload_unregister|spl_classes|spl_object_hash|splbool|spldoublylinkedlist|splenum|splfileinfo|splfileobject|splfixedarray|splfloat|splheap|splint|split|spliti|splmaxheap|splminheap|splobjectstorage|splobserver|splpriorityqueue|splqueue|splstack|splstring|splsubject|spltempfileobject|spoofchecker|sprintf|sql_regcase|sqlite3|sqlite3result|sqlite3stmt|sqlite_array_query|sqlite_busy_timeout|sqlite_changes|sqlite_close|sqlite_column|sqlite_create_aggregate|sqlite_create_function|sqlite_current|sqlite_error_string|sqlite_escape_string|sqlite_exec|sqlite_factory|sqlite_fetch_all|sqlite_fetch_array|sqlite_fetch_column_types|sqlite_fetch_object|sqlite_fetch_single|sqlite_fetch_string|sqlite_field_name|sqlite_has_more|sqlite_has_prev|sqlite_key|sqlite_last_error|sqlite_last_insert_rowid|sqlite_libencoding|sqlite_libversion|sqlite_next|sqlite_num_fields|sqlite_num_rows|sqlite_open|sqlite_popen|sqlite_prev|sqlite_query|sqlite_rewind|sqlite_seek|sqlite_single_query|sqlite_udf_decode_binary|sqlite_udf_encode_binary|sqlite_unbuffered_query|sqlite_valid|sqrt|srand|sscanf|ssdeep_fuzzy_compare|ssdeep_fuzzy_hash|ssdeep_fuzzy_hash_filename|ssh2_auth_hostbased_file|ssh2_auth_none|ssh2_auth_password|ssh2_auth_pubkey_file|ssh2_connect|ssh2_exec|ssh2_fetch_stream|ssh2_fingerprint|ssh2_methods_negotiated|ssh2_publickey_add|ssh2_publickey_init|ssh2_publickey_list|ssh2_publickey_remove|ssh2_scp_recv|ssh2_scp_send|ssh2_sftp|ssh2_sftp_lstat|ssh2_sftp_mkdir|ssh2_sftp_readlink|ssh2_sftp_realpath|ssh2_sftp_rename|ssh2_sftp_rmdir|ssh2_sftp_stat|ssh2_sftp_symlink|ssh2_sftp_unlink|ssh2_shell|ssh2_tunnel|stat|stats_absolute_deviation|stats_cdf_beta|stats_cdf_binomial|stats_cdf_cauchy|stats_cdf_chisquare|stats_cdf_exponential|stats_cdf_f|stats_cdf_gamma|stats_cdf_laplace|stats_cdf_logistic|stats_cdf_negative_binomial|stats_cdf_noncentral_chisquare|stats_cdf_noncentral_f|stats_cdf_poisson|stats_cdf_t|stats_cdf_uniform|stats_cdf_weibull|stats_covariance|stats_den_uniform|stats_dens_beta|stats_dens_cauchy|stats_dens_chisquare|stats_dens_exponential|stats_dens_f|stats_dens_gamma|stats_dens_laplace|stats_dens_logistic|stats_dens_negative_binomial|stats_dens_normal|stats_dens_pmf_binomial|stats_dens_pmf_hypergeometric|stats_dens_pmf_poisson|stats_dens_t|stats_dens_weibull|stats_harmonic_mean|stats_kurtosis|stats_rand_gen_beta|stats_rand_gen_chisquare|stats_rand_gen_exponential|stats_rand_gen_f|stats_rand_gen_funiform|stats_rand_gen_gamma|stats_rand_gen_ibinomial|stats_rand_gen_ibinomial_negative|stats_rand_gen_int|stats_rand_gen_ipoisson|stats_rand_gen_iuniform|stats_rand_gen_noncenral_chisquare|stats_rand_gen_noncentral_f|stats_rand_gen_noncentral_t|stats_rand_gen_normal|stats_rand_gen_t|stats_rand_get_seeds|stats_rand_phrase_to_seeds|stats_rand_ranf|stats_rand_setall|stats_skew|stats_standard_deviation|stats_stat_binomial_coef|stats_stat_correlation|stats_stat_gennch|stats_stat_independent_t|stats_stat_innerproduct|stats_stat_noncentral_t|stats_stat_paired_t|stats_stat_percentile|stats_stat_powersum|stats_variance|stomp|stomp_connect_error|stomp_version|stompexception|stompframe|str_getcsv|str_ireplace|str_pad|str_repeat|str_replace|str_rot13|str_shuffle|str_split|str_word_count|strcasecmp|strchr|strcmp|strcoll|strcspn|stream_bucket_append|stream_bucket_make_writeable|stream_bucket_new|stream_bucket_prepend|stream_context_create|stream_context_get_default|stream_context_get_options|stream_context_get_params|stream_context_set_default|stream_context_set_option|stream_context_set_params|stream_copy_to_stream|stream_encoding|stream_filter_append|stream_filter_prepend|stream_filter_register|stream_filter_remove|stream_get_contents|stream_get_filters|stream_get_line|stream_get_meta_data|stream_get_transports|stream_get_wrappers|stream_is_local|stream_notification_callback|stream_register_wrapper|stream_resolve_include_path|stream_select|stream_set_blocking|stream_set_read_buffer|stream_set_timeout|stream_set_write_buffer|stream_socket_accept|stream_socket_client|stream_socket_enable_crypto|stream_socket_get_name|stream_socket_pair|stream_socket_recvfrom|stream_socket_sendto|stream_socket_server|stream_socket_shutdown|stream_supports_lock|stream_wrapper_register|stream_wrapper_restore|stream_wrapper_unregister|streamwrapper|strftime|strip_tags|stripcslashes|stripos|stripslashes|stristr|strlen|strnatcasecmp|strnatcmp|strncasecmp|strncmp|strpbrk|strpos|strptime|strrchr|strrev|strripos|strrpos|strspn|strstr|strtok|strtolower|strtotime|strtoupper|strtr|strval|substr|substr_compare|substr_count|substr_replace|svm|svmmodel|svn_add|svn_auth_get_parameter|svn_auth_set_parameter|svn_blame|svn_cat|svn_checkout|svn_cleanup|svn_client_version|svn_commit|svn_delete|svn_diff|svn_export|svn_fs_abort_txn|svn_fs_apply_text|svn_fs_begin_txn2|svn_fs_change_node_prop|svn_fs_check_path|svn_fs_contents_changed|svn_fs_copy|svn_fs_delete|svn_fs_dir_entries|svn_fs_file_contents|svn_fs_file_length|svn_fs_is_dir|svn_fs_is_file|svn_fs_make_dir|svn_fs_make_file|svn_fs_node_created_rev|svn_fs_node_prop|svn_fs_props_changed|svn_fs_revision_prop|svn_fs_revision_root|svn_fs_txn_root|svn_fs_youngest_rev|svn_import|svn_log|svn_ls|svn_mkdir|svn_repos_create|svn_repos_fs|svn_repos_fs_begin_txn_for_commit|svn_repos_fs_commit_txn|svn_repos_hotcopy|svn_repos_open|svn_repos_recover|svn_revert|svn_status|svn_update|swf_actiongeturl|swf_actiongotoframe|swf_actiongotolabel|swf_actionnextframe|swf_actionplay|swf_actionprevframe|swf_actionsettarget|swf_actionstop|swf_actiontogglequality|swf_actionwaitforframe|swf_addbuttonrecord|swf_addcolor|swf_closefile|swf_definebitmap|swf_definefont|swf_defineline|swf_definepoly|swf_definerect|swf_definetext|swf_endbutton|swf_enddoaction|swf_endshape|swf_endsymbol|swf_fontsize|swf_fontslant|swf_fonttracking|swf_getbitmapinfo|swf_getfontinfo|swf_getframe|swf_labelframe|swf_lookat|swf_modifyobject|swf_mulcolor|swf_nextid|swf_oncondition|swf_openfile|swf_ortho|swf_ortho2|swf_perspective|swf_placeobject|swf_polarview|swf_popmatrix|swf_posround|swf_pushmatrix|swf_removeobject|swf_rotate|swf_scale|swf_setfont|swf_setframe|swf_shapearc|swf_shapecurveto|swf_shapecurveto3|swf_shapefillbitmapclip|swf_shapefillbitmaptile|swf_shapefilloff|swf_shapefillsolid|swf_shapelinesolid|swf_shapelineto|swf_shapemoveto|swf_showframe|swf_startbutton|swf_startdoaction|swf_startshape|swf_startsymbol|swf_textwidth|swf_translate|swf_viewport|swfaction|swfbitmap|swfbutton|swfdisplayitem|swffill|swffont|swffontchar|swfgradient|swfmorph|swfmovie|swfprebuiltclip|swfshape|swfsound|swfsoundinstance|swfsprite|swftext|swftextfield|swfvideostream|swish_construct|swish_getmetalist|swish_getpropertylist|swish_prepare|swish_query|swishresult_getmetalist|swishresult_stem|swishresults_getparsedwords|swishresults_getremovedstopwords|swishresults_nextresult|swishresults_seekresult|swishsearch_execute|swishsearch_resetlimit|swishsearch_setlimit|swishsearch_setphrasedelimiter|swishsearch_setsort|swishsearch_setstructure|sybase_affected_rows|sybase_close|sybase_connect|sybase_data_seek|sybase_deadlock_retry_count|sybase_fetch_array|sybase_fetch_assoc|sybase_fetch_field|sybase_fetch_object|sybase_fetch_row|sybase_field_seek|sybase_free_result|sybase_get_last_message|sybase_min_client_severity|sybase_min_error_severity|sybase_min_message_severity|sybase_min_server_severity|sybase_num_fields|sybase_num_rows|sybase_pconnect|sybase_query|sybase_result|sybase_select_db|sybase_set_message_handler|sybase_unbuffered_query|symlink|sys_get_temp_dir|sys_getloadavg|syslog|system|tag|tan|tanh|tcpwrap_check|tempnam|textdomain|tidy|tidy_access_count|tidy_config_count|tidy_diagnose|tidy_error_count|tidy_get_error_buffer|tidy_get_output|tidy_load_config|tidy_reset_config|tidy_save_config|tidy_set_encoding|tidy_setopt|tidy_warning_count|tidynode|time|time_nanosleep|time_sleep_until|timezone_abbreviations_list|timezone_identifiers_list|timezone_location_get|timezone_name_from_abbr|timezone_name_get|timezone_offset_get|timezone_open|timezone_transitions_get|timezone_version_get|tmpfile|token_get_all|token_name|tokyotyrant|tokyotyrantquery|tokyotyranttable|tostring|tostring|touch|trait_exists|transliterator|traversable|trigger_error|trim|uasort|ucfirst|ucwords|udm_add_search_limit|udm_alloc_agent|udm_alloc_agent_array|udm_api_version|udm_cat_list|udm_cat_path|udm_check_charset|udm_check_stored|udm_clear_search_limits|udm_close_stored|udm_crc32|udm_errno|udm_error|udm_find|udm_free_agent|udm_free_ispell_data|udm_free_res|udm_get_doc_count|udm_get_res_field|udm_get_res_param|udm_hash32|udm_load_ispell_data|udm_open_stored|udm_set_agent_param|uksort|umask|underflowexception|unexpectedvalueexception|uniqid|unixtojd|unlink|unpack|unregister_tick_function|unserialize|unset|urldecode|urlencode|use_soap_error_handler|user_error|usleep|usort|utf8_decode|utf8_encode|v8js|v8jsexception|var_dump|var_export|variant|variant_abs|variant_add|variant_and|variant_cast|variant_cat|variant_cmp|variant_date_from_timestamp|variant_date_to_timestamp|variant_div|variant_eqv|variant_fix|variant_get_type|variant_idiv|variant_imp|variant_int|variant_mod|variant_mul|variant_neg|variant_not|variant_or|variant_pow|variant_round|variant_set|variant_set_type|variant_sub|variant_xor|version_compare|vfprintf|virtual|vpopmail_add_alias_domain|vpopmail_add_alias_domain_ex|vpopmail_add_domain|vpopmail_add_domain_ex|vpopmail_add_user|vpopmail_alias_add|vpopmail_alias_del|vpopmail_alias_del_domain|vpopmail_alias_get|vpopmail_alias_get_all|vpopmail_auth_user|vpopmail_del_domain|vpopmail_del_domain_ex|vpopmail_del_user|vpopmail_error|vpopmail_passwd|vpopmail_set_user_quota|vprintf|vsprintf|w32api_deftype|w32api_init_dtype|w32api_invoke_function|w32api_register_function|w32api_set_call_method|wddx_add_vars|wddx_deserialize|wddx_packet_end|wddx_packet_start|wddx_serialize_value|wddx_serialize_vars|win32_continue_service|win32_create_service|win32_delete_service|win32_get_last_control_message|win32_pause_service|win32_ps_list_procs|win32_ps_stat_mem|win32_ps_stat_proc|win32_query_service_status|win32_set_service_status|win32_start_service|win32_start_service_ctrl_dispatcher|win32_stop_service|wincache_fcache_fileinfo|wincache_fcache_meminfo|wincache_lock|wincache_ocache_fileinfo|wincache_ocache_meminfo|wincache_refresh_if_changed|wincache_rplist_fileinfo|wincache_rplist_meminfo|wincache_scache_info|wincache_scache_meminfo|wincache_ucache_add|wincache_ucache_cas|wincache_ucache_clear|wincache_ucache_dec|wincache_ucache_delete|wincache_ucache_exists|wincache_ucache_get|wincache_ucache_inc|wincache_ucache_info|wincache_ucache_meminfo|wincache_ucache_set|wincache_unlock|wordwrap|xattr_get|xattr_list|xattr_remove|xattr_set|xattr_supported|xdiff_file_bdiff|xdiff_file_bdiff_size|xdiff_file_bpatch|xdiff_file_diff|xdiff_file_diff_binary|xdiff_file_merge3|xdiff_file_patch|xdiff_file_patch_binary|xdiff_file_rabdiff|xdiff_string_bdiff|xdiff_string_bdiff_size|xdiff_string_bpatch|xdiff_string_diff|xdiff_string_diff_binary|xdiff_string_merge3|xdiff_string_patch|xdiff_string_patch_binary|xdiff_string_rabdiff|xhprof_disable|xhprof_enable|xhprof_sample_disable|xhprof_sample_enable|xml_error_string|xml_get_current_byte_index|xml_get_current_column_number|xml_get_current_line_number|xml_get_error_code|xml_parse|xml_parse_into_struct|xml_parser_create|xml_parser_create_ns|xml_parser_free|xml_parser_get_option|xml_parser_set_option|xml_set_character_data_handler|xml_set_default_handler|xml_set_element_handler|xml_set_end_namespace_decl_handler|xml_set_external_entity_ref_handler|xml_set_notation_decl_handler|xml_set_object|xml_set_processing_instruction_handler|xml_set_start_namespace_decl_handler|xml_set_unparsed_entity_decl_handler|xmlreader|xmlrpc_decode|xmlrpc_decode_request|xmlrpc_encode|xmlrpc_encode_request|xmlrpc_get_type|xmlrpc_is_fault|xmlrpc_parse_method_descriptions|xmlrpc_server_add_introspection_data|xmlrpc_server_call_method|xmlrpc_server_create|xmlrpc_server_destroy|xmlrpc_server_register_introspection_callback|xmlrpc_server_register_method|xmlrpc_set_type|xmlwriter_end_attribute|xmlwriter_end_cdata|xmlwriter_end_comment|xmlwriter_end_document|xmlwriter_end_dtd|xmlwriter_end_dtd_attlist|xmlwriter_end_dtd_element|xmlwriter_end_dtd_entity|xmlwriter_end_element|xmlwriter_end_pi|xmlwriter_flush|xmlwriter_full_end_element|xmlwriter_open_memory|xmlwriter_open_uri|xmlwriter_output_memory|xmlwriter_set_indent|xmlwriter_set_indent_string|xmlwriter_start_attribute|xmlwriter_start_attribute_ns|xmlwriter_start_cdata|xmlwriter_start_comment|xmlwriter_start_document|xmlwriter_start_dtd|xmlwriter_start_dtd_attlist|xmlwriter_start_dtd_element|xmlwriter_start_dtd_entity|xmlwriter_start_element|xmlwriter_start_element_ns|xmlwriter_start_pi|xmlwriter_text|xmlwriter_write_attribute|xmlwriter_write_attribute_ns|xmlwriter_write_cdata|xmlwriter_write_comment|xmlwriter_write_dtd|xmlwriter_write_dtd_attlist|xmlwriter_write_dtd_element|xmlwriter_write_dtd_entity|xmlwriter_write_element|xmlwriter_write_element_ns|xmlwriter_write_pi|xmlwriter_write_raw|xpath_eval|xpath_eval_expression|xpath_new_context|xpath_register_ns|xpath_register_ns_auto|xptr_eval|xptr_new_context|xslt_backend_info|xslt_backend_name|xslt_backend_version|xslt_create|xslt_errno|xslt_error|xslt_free|xslt_getopt|xslt_process|xslt_set_base|xslt_set_encoding|xslt_set_error_handler|xslt_set_log|xslt_set_object|xslt_set_sax_handler|xslt_set_sax_handlers|xslt_set_scheme_handler|xslt_set_scheme_handlers|xslt_setopt|xsltprocessor|yaml_emit|yaml_emit_file|yaml_parse|yaml_parse_file|yaml_parse_url|yaz_addinfo|yaz_ccl_conf|yaz_ccl_parse|yaz_close|yaz_connect|yaz_database|yaz_element|yaz_errno|yaz_error|yaz_es|yaz_es_result|yaz_get_option|yaz_hits|yaz_itemorder|yaz_present|yaz_range|yaz_record|yaz_scan|yaz_scan_result|yaz_schema|yaz_search|yaz_set_option|yaz_sort|yaz_syntax|yaz_wait|yp_all|yp_cat|yp_err_string|yp_errno|yp_first|yp_get_default_domain|yp_master|yp_match|yp_next|yp_order|zend_logo_guid|zend_thread_id|zend_version|zip_close|zip_entry_close|zip_entry_compressedsize|zip_entry_compressionmethod|zip_entry_filesize|zip_entry_name|zip_entry_open|zip_entry_read|zip_open|zip_read|ziparchive|ziparchive_addemptydir|ziparchive_addfile|ziparchive_addfromstring|ziparchive_close|ziparchive_deleteindex|ziparchive_deletename|ziparchive_extractto|ziparchive_getarchivecomment|ziparchive_getcommentindex|ziparchive_getcommentname|ziparchive_getfromindex|ziparchive_getfromname|ziparchive_getnameindex|ziparchive_getstatusstring|ziparchive_getstream|ziparchive_locatename|ziparchive_open|ziparchive_renameindex|ziparchive_renamename|ziparchive_setCommentName|ziparchive_setarchivecomment|ziparchive_setcommentindex|ziparchive_statindex|ziparchive_statname|ziparchive_unchangeall|ziparchive_unchangearchive|ziparchive_unchangeindex|ziparchive_unchangename|zlib_get_coding_type".split("|")),n=i.arrayToMap("abstract|and|array|as|break|case|catch|class|clone|const|continue|declare|default|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|final|for|foreach|function|global|goto|if|implements|interface|instanceof|namespace|new|or|private|protected|public|static|switch|throw|trait|try|use|var|while|xor".split("|")),r=i.arrayToMap("die|echo|empty|exit|eval|include|include_once|isset|list|require|require_once|return|print|unset".split("|")),o=i.arrayToMap("true|TRUE|false|FALSE|null|NULL|__CLASS__|__DIR__|__FILE__|__LINE__|__METHOD__|__FUNCTION__|__NAMESPACE__".split("|")),u=i.arrayToMap("$GLOBALS|$_SERVER|$_GET|$_POST|$_FILES|$_REQUEST|$_SESSION|$_ENV|$_COOKIE|$php_errormsg|$HTTP_RAW_POST_DATA|$http_response_header|$argc|$argv".split("|")),a=i.arrayToMap("key_exists|cairo_matrix_create_scale|cairo_matrix_create_translate|call_user_method|call_user_method_array|com_addref|com_get|com_invoke|com_isenum|com_load|com_release|com_set|connection_timeout|cubrid_load_from_glo|cubrid_new_glo|cubrid_save_to_glo|cubrid_send_glo|define_syslog_variables|dl|ereg|ereg_replace|eregi|eregi_replace|hw_documentattributes|hw_documentbodytag|hw_documentsize|hw_outputdocument|imagedashedline|maxdb_bind_param|maxdb_bind_result|maxdb_client_encoding|maxdb_close_long_data|maxdb_execute|maxdb_fetch|maxdb_get_metadata|maxdb_param_count|maxdb_send_long_data|mcrypt_ecb|mcrypt_generic_end|mime_content_type|mysql_createdb|mysql_dbname|mysql_db_query|mysql_drop_db|mysql_dropdb|mysql_escape_string|mysql_fieldflags|mysql_fieldflags|mysql_fieldname|mysql_fieldtable|mysql_fieldtype|mysql_freeresult|mysql_listdbs|mysql_list_fields|mysql_listfields|mysql_list_tables|mysql_listtables|mysql_numfields|mysql_numrows|mysql_selectdb|mysql_tablename|mysqli_bind_param|mysqli_bind_result|mysqli_disable_reads_from_master|mysqli_disable_rpl_parse|mysqli_enable_reads_from_master|mysqli_enable_rpl_parse|mysqli_execute|mysqli_fetch|mysqli_get_metadata|mysqli_master_query|mysqli_param_count|mysqli_rpl_parse_enabled|mysqli_rpl_probe|mysqli_rpl_query_type|mysqli_send_long_data|mysqli_send_query|mysqli_slave_query|ocibindbyname|ocicancel|ocicloselob|ocicollappend|ocicollassign|ocicollassignelem|ocicollgetelem|ocicollmax|ocicollsize|ocicolltrim|ocicolumnisnull|ocicolumnname|ocicolumnprecision|ocicolumnscale|ocicolumnsize|ocicolumntype|ocicolumntyperaw|ocicommit|ocidefinebyname|ocierror|ociexecute|ocifetch|ocifetchinto|ocifetchstatement|ocifreecollection|ocifreecursor|ocifreedesc|ocifreestatement|ociinternaldebug|ociloadlob|ocilogoff|ocilogon|ocinewcollection|ocinewcursor|ocinewdescriptor|ocinlogon|ocinumcols|ociparse|ociplogon|ociresult|ocirollback|ocirowcount|ocisavelob|ocisavelobfile|ociserverversion|ocisetprefetch|ocistatementtype|ociwritelobtofile|ociwritetemporarylob|PDF_add_annotation|PDF_add_bookmark|PDF_add_launchlink|PDF_add_locallink|PDF_add_note|PDF_add_outline|PDF_add_pdflink|PDF_add_weblink|PDF_attach_file|PDF_begin_page|PDF_begin_template|PDF_close_pdi|PDF_close|PDF_findfont|PDF_get_font|PDF_get_fontname|PDF_get_fontsize|PDF_get_image_height|PDF_get_image_width|PDF_get_majorversion|PDF_get_minorversion|PDF_get_pdi_parameter|PDF_get_pdi_value|PDF_open_ccitt|PDF_open_file|PDF_open_gif|PDF_open_image_file|PDF_open_image|PDF_open_jpeg|PDF_open_pdi|PDF_open_tiff|PDF_place_image|PDF_place_pdi_page|PDF_set_border_color|PDF_set_border_dash|PDF_set_border_style|PDF_set_char_spacing|PDF_set_duration|PDF_set_horiz_scaling|PDF_set_info_author|PDF_set_info_creator|PDF_set_info_keywords|PDF_set_info_subject|PDF_set_info_title|PDF_set_leading|PDF_set_text_matrix|PDF_set_text_rendering|PDF_set_text_rise|PDF_set_word_spacing|PDF_setgray_fill|PDF_setgray_stroke|PDF_setgray|PDF_setpolydash|PDF_setrgbcolor_fill|PDF_setrgbcolor_stroke|PDF_setrgbcolor|PDF_show_boxed|php_check_syntax|px_set_tablename|px_set_targetencoding|runkit_sandbox_output_handler|session_is_registered|session_register|session_unregisterset_magic_quotes_runtime|magic_quotes_runtime|set_socket_blocking|socket_set_blocking|set_socket_timeout|socket_set_timeout|split|spliti|sql_regcase".split("|")),f=i.arrayToMap("cfunction|old_function".split("|")),l=i.arrayToMap([]);this.$rules={start:[{token:"comment",regex:/(?:#|\/\/)(?:[^?]|\?[^>])*/},e.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/][gimy]*\\s*(?=[).,;]|$)"},{token:"string",regex:'"',next:"qqstring"},{token:"string",regex:"'",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language",regex:"\\b(?:DEFAULT_INCLUDE_PATH|E_(?:ALL|CO(?:MPILE_(?:ERROR|WARNING)|RE_(?:ERROR|WARNING))|ERROR|NOTICE|PARSE|STRICT|USER_(?:ERROR|NOTICE|WARNING)|WARNING)|P(?:EAR_(?:EXTENSION_DIR|INSTALL_DIR)|HP_(?:BINDIR|CONFIG_FILE_(?:PATH|SCAN_DIR)|DATADIR|E(?:OL|XTENSION_DIR)|INT_(?:MAX|SIZE)|L(?:IBDIR|OCALSTATEDIR)|O(?:S|UTPUT_HANDLER_(?:CONT|END|START))|PREFIX|S(?:API|HLIB_SUFFIX|YSCONFDIR)|VERSION))|__COMPILER_HALT_OFFSET__)\\b"},{token:["keyword","text","support.class"],regex:"\\b(new)(\\s+)(\\w+)"},{token:["support.class","keyword.operator"],regex:"\\b(\\w+)(::)"},{token:"constant.language",regex:"\\b(?:A(?:B(?:DAY_(?:1|2|3|4|5|6|7)|MON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9))|LT_DIGITS|M_STR|SSERT_(?:ACTIVE|BAIL|CALLBACK|QUIET_EVAL|WARNING))|C(?:ASE_(?:LOWER|UPPER)|HAR_MAX|O(?:DESET|NNECTION_(?:ABORTED|NORMAL|TIMEOUT)|UNT_(?:NORMAL|RECURSIVE))|R(?:EDITS_(?:ALL|DOCS|FULLPAGE|G(?:ENERAL|ROUP)|MODULES|QA|SAPI)|NCYSTR|YPT_(?:BLOWFISH|EXT_DES|MD5|S(?:ALT_LENGTH|TD_DES)))|URRENCY_SYMBOL)|D(?:AY_(?:1|2|3|4|5|6|7)|ECIMAL_POINT|IRECTORY_SEPARATOR|_(?:FMT|T_FMT))|E(?:NT_(?:COMPAT|NOQUOTES|QUOTES)|RA(?:_(?:D_(?:FMT|T_FMT)|T_FMT|YEAR)|)|XTR_(?:IF_EXISTS|OVERWRITE|PREFIX_(?:ALL|I(?:F_EXISTS|NVALID)|SAME)|SKIP))|FRAC_DIGITS|GROUPING|HTML_(?:ENTITIES|SPECIALCHARS)|IN(?:FO_(?:ALL|C(?:ONFIGURATION|REDITS)|ENVIRONMENT|GENERAL|LICENSE|MODULES|VARIABLES)|I_(?:ALL|PERDIR|SYSTEM|USER)|T_(?:CURR_SYMBOL|FRAC_DIGITS))|L(?:C_(?:ALL|C(?:OLLATE|TYPE)|M(?:ESSAGES|ONETARY)|NUMERIC|TIME)|O(?:CK_(?:EX|NB|SH|UN)|G_(?:A(?:LERT|UTH(?:PRIV|))|C(?:ONS|R(?:IT|ON))|D(?:AEMON|EBUG)|E(?:MERG|RR)|INFO|KERN|L(?:OCAL(?:0|1|2|3|4|5|6|7)|PR)|MAIL|N(?:DELAY|EWS|O(?:TICE|WAIT))|ODELAY|P(?:ERROR|ID)|SYSLOG|U(?:SER|UCP)|WARNING)))|M(?:ON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9|DECIMAL_POINT|GROUPING|THOUSANDS_SEP)|_(?:1_PI|2_(?:PI|SQRTPI)|E|L(?:N(?:10|2)|OG(?:10E|2E))|PI(?:_(?:2|4)|)|SQRT(?:1_2|2)))|N(?:EGATIVE_SIGN|O(?:EXPR|STR)|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|P(?:ATH(?:INFO_(?:BASENAME|DIRNAME|EXTENSION)|_SEPARATOR)|M_STR|OSITIVE_SIGN|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|RADIXCHAR|S(?:EEK_(?:CUR|END|SET)|ORT_(?:ASC|DESC|NUMERIC|REGULAR|STRING)|TR_PAD_(?:BOTH|LEFT|RIGHT))|T(?:HOUS(?:ANDS_SEP|EP)|_FMT(?:_AMPM|))|YES(?:EXPR|STR)|STD(?:IN|OUT|ERR))\\b"},{token:function(e){return n.hasOwnProperty(e)?"keyword":o.hasOwnProperty(e)?"constant.language":u.hasOwnProperty(e)?"variable.language":l.hasOwnProperty(e)?"invalid.illegal":t.hasOwnProperty(e)?"support.function":e=="debugger"?"invalid.deprecated":e.match(/^(\$[a-zA-Z_\x7f-\uffff][a-zA-Z0-9_\x7f-\uffff]*|self|parent)$/)?"variable":"identifier"},regex:/[a-zA-Z_$\x7f-\uffff][a-zA-Z0-9_\x7f-\uffff]*/},{onMatch:function(e,t,n){e=e.substr(3);if(e[0]=="'"||e[0]=='"')e=e.slice(1,-1);return n.unshift(this.next,e),"markup.list"},regex:/<<<(?:\w+|'\w+'|"\w+")$/,next:"heredoc"},{token:"keyword.operator",regex:"::|!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|!=|!==|<=|>=|=>|<<=|>>=|>>>=|<>|<|>|=|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],heredoc:[{onMatch:function(e,t,n){return n[1]!=e?"string":(n.shift(),n.shift(),"markup.list")},regex:"^\\w+(?=;?$)",next:"start"},{token:"string",regex:".*"}],comment:[{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment"}],qqstring:[{token:"constant.language.escape",regex:'\\\\(?:[nrtvef\\\\"$]|[0-7]{1,3}|x[0-9A-Fa-f]{1,2})'},{token:"variable",regex:/\$[\w]+(?:\[[\w\]+]|[=\-]>\w+)?/},{token:"variable",regex:/\$\{[^"\}]+\}?/},{token:"string",regex:'"',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:/\\['\\]/},{token:"string",regex:"'",next:"start"},{defaultToken:"string"}]},this.embedRules(s,"doc-",[s.getEndRule("start")])};r.inherits(a,o);var f=function(){u.call(this);var e=[{token:"support.php_tag",regex:"<\\?(?:php|=)?",push:"php-start"}],t=[{token:"support.php_tag",regex:"\\?>",next:"pop"}];for(var n in this.$rules)this.$rules[n].unshift.apply(this.$rules[n],e);this.embedRules(a,"php-",t,["start"]),this.normalizeRules()};r.inherits(f,u),t.PhpHighlightRules=f,t.PhpLangHighlightRules=a}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/php_completions",["require","exports","module"],function(e,t,n){"use strict";function s(e,t){return e.type.lastIndexOf(t)>-1}var r={abs:["int abs(int number)","Return the absolute value of the number"],acos:["float acos(float number)","Return the arc cosine of the number in radians"],acosh:["float acosh(float number)","Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number"],addGlob:["bool addGlob(string pattern[,int flags [, array options]])","Add files matching the glob pattern. See php's glob for the pattern syntax."],addPattern:["bool addPattern(string pattern[, string path [, array options]])","Add files matching the pcre pattern. See php's pcre for the pattern syntax."],addcslashes:["string addcslashes(string str, string charlist)","Escapes all chars mentioned in charlist with backslash. It creates octal representations if asked to backslash characters with 8th bit set or with ASCII<32 (except '\\n', '\\r', '\\t' etc...)"],addslashes:["string addslashes(string str)","Escapes single quote, double quotes and backslash characters in a string with backslashes"],apache_child_terminate:["bool apache_child_terminate(void)","Terminate apache process after this request"],apache_get_modules:["array apache_get_modules(void)","Get a list of loaded Apache modules"],apache_get_version:["string apache_get_version(void)","Fetch Apache version"],apache_getenv:["bool apache_getenv(string variable [, bool walk_to_top])","Get an Apache subprocess_env variable"],apache_lookup_uri:["object apache_lookup_uri(string URI)","Perform a partial request of the given URI to obtain information about it"],apache_note:["string apache_note(string note_name [, string note_value])","Get and set Apache request notes"],apache_request_auth_name:["string apache_request_auth_name()",""],apache_request_auth_type:["string apache_request_auth_type()",""],apache_request_discard_request_body:["long apache_request_discard_request_body()",""],apache_request_err_headers_out:["array apache_request_err_headers_out([{string name|array list} [, string value [, bool replace = false]]])","* fetch all headers that go out in case of an error or a subrequest"],apache_request_headers:["array apache_request_headers(void)","Fetch all HTTP request headers"],apache_request_headers_in:["array apache_request_headers_in()","* fetch all incoming request headers"],apache_request_headers_out:["array apache_request_headers_out([{string name|array list} [, string value [, bool replace = false]]])","* fetch all outgoing request headers"],apache_request_is_initial_req:["bool apache_request_is_initial_req()",""],apache_request_log_error:["boolean apache_request_log_error(string message, [long facility])",""],apache_request_meets_conditions:["long apache_request_meets_conditions()",""],apache_request_remote_host:["int apache_request_remote_host([int type])",""],apache_request_run:["long apache_request_run()","This is a wrapper for ap_sub_run_req and ap_destory_sub_req. It takes sub_request, runs it, destroys it, and returns it's status."],apache_request_satisfies:["long apache_request_satisfies()",""],apache_request_server_port:["int apache_request_server_port()",""],apache_request_set_etag:["void apache_request_set_etag()",""],apache_request_set_last_modified:["void apache_request_set_last_modified()",""],apache_request_some_auth_required:["bool apache_request_some_auth_required()",""],apache_request_sub_req_lookup_file:["object apache_request_sub_req_lookup_file(string file)","Returns sub-request for the specified file. You would need to run it yourself with run()."],apache_request_sub_req_lookup_uri:["object apache_request_sub_req_lookup_uri(string uri)","Returns sub-request for the specified uri. You would need to run it yourself with run()"],apache_request_sub_req_method_uri:["object apache_request_sub_req_method_uri(string method, string uri)","Returns sub-request for the specified file. You would need to run it yourself with run()."],apache_request_update_mtime:["long apache_request_update_mtime([int dependency_mtime])",""],apache_reset_timeout:["bool apache_reset_timeout(void)","Reset the Apache write timer"],apache_response_headers:["array apache_response_headers(void)","Fetch all HTTP response headers"],apache_setenv:["bool apache_setenv(string variable, string value [, bool walk_to_top])","Set an Apache subprocess_env variable"],array_change_key_case:["array array_change_key_case(array input [, int case=CASE_LOWER])","Retuns an array with all string keys lowercased [or uppercased]"],array_chunk:["array array_chunk(array input, int size [, bool preserve_keys])","Split array into chunks"],array_combine:["array array_combine(array keys, array values)","Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values"],array_count_values:["array array_count_values(array input)","Return the value as key and the frequency of that value in input as value"],array_diff:["array array_diff(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are not present in any of the others arguments."],array_diff_assoc:["array array_diff_assoc(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal"],array_diff_key:["array array_diff_key(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved."],array_diff_uassoc:["array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function."],array_diff_ukey:["array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)","Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved."],array_fill:["array array_fill(int start_key, int num, mixed val)","Create an array containing num elements starting with index start_key each initialized to val"],array_fill_keys:["array array_fill_keys(array keys, mixed val)","Create an array using the elements of the first parameter as keys each initialized to val"],array_filter:["array array_filter(array input [, mixed callback])","Filters elements from the array via the callback."],array_flip:["array array_flip(array input)","Return array with key <-> value flipped"],array_intersect:["array array_intersect(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are present in all the other arguments"],array_intersect_assoc:["array array_intersect_assoc(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check"],array_intersect_key:["array array_intersect_key(array arr1, array arr2 [, array ...])","Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data."],array_intersect_uassoc:["array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using an user-supplied callback."],array_intersect_ukey:["array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)","Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data."],array_key_exists:["bool array_key_exists(mixed key, array search)","Checks if the given key or index exists in the array"],array_keys:["array array_keys(array input [, mixed search_value[, bool strict]])","Return just the keys from the input array, optionally only for the specified search_value"],array_map:["array array_map(mixed callback, array input1 [, array input2 ,...])","Applies the callback to the elements in given arrays."],array_merge:["array array_merge(array arr1, array arr2 [, array ...])","Merges elements from passed arrays into one array"],array_merge_recursive:["array array_merge_recursive(array arr1, array arr2 [, array ...])","Recursively merges elements from passed arrays into one array"],array_multisort:["bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]], ...])","Sort multiple arrays at once similar to how ORDER BY clause works in SQL"],array_pad:["array array_pad(array input, int pad_size, mixed pad_value)","Returns a copy of input array padded with pad_value to size pad_size"],array_pop:["mixed array_pop(array stack)","Pops an element off the end of the array"],array_product:["mixed array_product(array input)","Returns the product of the array entries"],array_push:["int array_push(array stack, mixed var [, mixed ...])","Pushes elements onto the end of the array"],array_rand:["mixed array_rand(array input [, int num_req])","Return key/keys for random entry/entries in the array"],array_reduce:["mixed array_reduce(array input, mixed callback [, mixed initial])","Iteratively reduce the array to a single value via the callback."],array_replace:["array array_replace(array arr1, array arr2 [, array ...])","Replaces elements from passed arrays into one array"],array_replace_recursive:["array array_replace_recursive(array arr1, array arr2 [, array ...])","Recursively replaces elements from passed arrays into one array"],array_reverse:["array array_reverse(array input [, bool preserve keys])","Return input as a new array with the order of the entries reversed"],array_search:["mixed array_search(mixed needle, array haystack [, bool strict])","Searches the array for a given value and returns the corresponding key if successful"],array_shift:["mixed array_shift(array stack)","Pops an element off the beginning of the array"],array_slice:["array array_slice(array input, int offset [, int length [, bool preserve_keys]])","Returns elements specified by offset and length"],array_splice:["array array_splice(array input, int offset [, int length [, array replacement]])","Removes the elements designated by offset and length and replace them with supplied array"],array_sum:["mixed array_sum(array input)","Returns the sum of the array entries"],array_udiff:["array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function."],array_udiff_assoc:["array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function."],array_udiff_uassoc:["array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)","Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions."],array_uintersect:["array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using an user-supplied callback."],array_uintersect_assoc:["array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using an user-supplied callback."],array_uintersect_uassoc:["array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)","Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks."],array_unique:["array array_unique(array input [, int sort_flags])","Removes duplicate values from array"],array_unshift:["int array_unshift(array stack, mixed var [, mixed ...])","Pushes elements onto the beginning of the array"],array_values:["array array_values(array input)","Return just the values from the input array"],array_walk:["bool array_walk(array input, string funcname [, mixed userdata])","Apply a user function to every member of an array"],array_walk_recursive:["bool array_walk_recursive(array input, string funcname [, mixed userdata])","Apply a user function recursively to every member of an array"],arsort:["bool arsort(array &array_arg [, int sort_flags])","Sort an array in reverse order and maintain index association"],asin:["float asin(float number)","Returns the arc sine of the number in radians"],asinh:["float asinh(float number)","Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number"],asort:["bool asort(array &array_arg [, int sort_flags])","Sort an array and maintain index association"],assert:["int assert(string|bool assertion)","Checks if assertion is false"],assert_options:["mixed assert_options(int what [, mixed value])","Set/get the various assert flags"],atan:["float atan(float number)","Returns the arc tangent of the number in radians"],atan2:["float atan2(float y, float x)","Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x"],atanh:["float atanh(float number)","Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number"],attachIterator:["void attachIterator(Iterator iterator[, mixed info])","Attach a new iterator"],base64_decode:["string base64_decode(string str[, bool strict])","Decodes string using MIME base64 algorithm"],base64_encode:["string base64_encode(string str)","Encodes string using MIME base64 algorithm"],base_convert:["string base_convert(string number, int frombase, int tobase)","Converts a number in a string from any base <= 36 to any base <= 36"],basename:["string basename(string path [, string suffix])","Returns the filename component of the path"],bcadd:["string bcadd(string left_operand, string right_operand [, int scale])","Returns the sum of two arbitrary precision numbers"],bccomp:["int bccomp(string left_operand, string right_operand [, int scale])","Compares two arbitrary precision numbers"],bcdiv:["string bcdiv(string left_operand, string right_operand [, int scale])","Returns the quotient of two arbitrary precision numbers (division)"],bcmod:["string bcmod(string left_operand, string right_operand)","Returns the modulus of the two arbitrary precision operands"],bcmul:["string bcmul(string left_operand, string right_operand [, int scale])","Returns the multiplication of two arbitrary precision numbers"],bcpow:["string bcpow(string x, string y [, int scale])","Returns the value of an arbitrary precision number raised to the power of another"],bcpowmod:["string bcpowmod(string x, string y, string mod [, int scale])","Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous"],bcscale:["bool bcscale(int scale)","Sets default scale parameter for all bc math functions"],bcsqrt:["string bcsqrt(string operand [, int scale])","Returns the square root of an arbitray precision number"],bcsub:["string bcsub(string left_operand, string right_operand [, int scale])","Returns the difference between two arbitrary precision numbers"],bin2hex:["string bin2hex(string data)","Converts the binary representation of data to hex"],bind_textdomain_codeset:["string bind_textdomain_codeset (string domain, string codeset)","Specify the character encoding in which the messages from the DOMAIN message catalog will be returned."],bindec:["int bindec(string binary_number)","Returns the decimal equivalent of the binary number"],bindtextdomain:["string bindtextdomain(string domain_name, string dir)","Bind to the text domain domain_name, looking for translations in dir. Returns the current domain"],birdstep_autocommit:["bool birdstep_autocommit(int index)",""],birdstep_close:["bool birdstep_close(int id)",""],birdstep_commit:["bool birdstep_commit(int index)",""],birdstep_connect:["int birdstep_connect(string server, string user, string pass)",""],birdstep_exec:["int birdstep_exec(int index, string exec_str)",""],birdstep_fetch:["bool birdstep_fetch(int index)",""],birdstep_fieldname:["string birdstep_fieldname(int index, int col)",""],birdstep_fieldnum:["int birdstep_fieldnum(int index)",""],birdstep_freeresult:["bool birdstep_freeresult(int index)",""],birdstep_off_autocommit:["bool birdstep_off_autocommit(int index)",""],birdstep_result:["mixed birdstep_result(int index, mixed col)",""],birdstep_rollback:["bool birdstep_rollback(int index)",""],bzcompress:["string bzcompress(string source [, int blocksize100k [, int workfactor]])","Compresses a string into BZip2 encoded data"],bzdecompress:["string bzdecompress(string source [, int small])","Decompresses BZip2 compressed data"],bzerrno:["int bzerrno(resource bz)","Returns the error number"],bzerror:["array bzerror(resource bz)","Returns the error number and error string in an associative array"],bzerrstr:["string bzerrstr(resource bz)","Returns the error string"],bzopen:["resource bzopen(string|int file|fp, string mode)","Opens a new BZip2 stream"],bzread:["string bzread(resource bz[, int length])","Reads up to length bytes from a BZip2 stream, or 1024 bytes if length is not specified"],cal_days_in_month:["int cal_days_in_month(int calendar, int month, int year)","Returns the number of days in a month for a given year and calendar"],cal_from_jd:["array cal_from_jd(int jd, int calendar)","Converts from Julian Day Count to a supported calendar and return extended information"],cal_info:["array cal_info([int calendar])","Returns information about a particular calendar"],cal_to_jd:["int cal_to_jd(int calendar, int month, int day, int year)","Converts from a supported calendar to Julian Day Count"],call_user_func:["mixed call_user_func(mixed function_name [, mixed parmeter] [, mixed ...])","Call a user function which is the first parameter"],call_user_func_array:["mixed call_user_func_array(string function_name, array parameters)","Call a user function which is the first parameter with the arguments contained in array"],call_user_method:["mixed call_user_method(string method_name, mixed object [, mixed parameter] [, mixed ...])","Call a user method on a specific object or class"],call_user_method_array:["mixed call_user_method_array(string method_name, mixed object, array params)","Call a user method on a specific object or class using a parameter array"],ceil:["float ceil(float number)","Returns the next highest integer value of the number"],chdir:["bool chdir(string directory)","Change the current directory"],checkdate:["bool checkdate(int month, int day, int year)","Returns true(1) if it is a valid date in gregorian calendar"],chgrp:["bool chgrp(string filename, mixed group)","Change file group"],chmod:["bool chmod(string filename, int mode)","Change file mode"],chown:["bool chown (string filename, mixed user)","Change file owner"],chr:["string chr(int ascii)","Converts ASCII code to a character"],chroot:["bool chroot(string directory)","Change root directory"],chunk_split:["string chunk_split(string str [, int chunklen [, string ending]])","Returns split line"],class_alias:["bool class_alias(string user_class_name , string alias_name [, bool autoload])","Creates an alias for user defined class"],class_exists:["bool class_exists(string classname [, bool autoload])","Checks if the class exists"],class_implements:["array class_implements(mixed what [, bool autoload ])","Return all classes and interfaces implemented by SPL"],class_parents:["array class_parents(object instance [, boolean autoload = true])","Return an array containing the names of all parent classes"],clearstatcache:["void clearstatcache([bool clear_realpath_cache[, string filename]])","Clear file stat cache"],closedir:["void closedir([resource dir_handle])","Close directory connection identified by the dir_handle"],closelog:["bool closelog(void)","Close connection to system logger"],collator_asort:["bool collator_asort( Collator $coll, array(string) $arr )","* Sort array using specified collator, maintaining index association."],collator_compare:["int collator_compare( Collator $coll, string $str1, string $str2 )","* Compare two strings."],collator_create:["Collator collator_create( string $locale )","* Create collator."],collator_get_attribute:["int collator_get_attribute( Collator $coll, int $attr )","* Get collation attribute value."],collator_get_error_code:["int collator_get_error_code( Collator $coll )","* Get collator's last error code."],collator_get_error_message:["string collator_get_error_message( Collator $coll )","* Get text description for collator's last error code."],collator_get_locale:["string collator_get_locale( Collator $coll, int $type )","* Gets the locale name of the collator."],collator_get_sort_key:["bool collator_get_sort_key( Collator $coll, string $str )","* Get a sort key for a string from a Collator. }}}"],collator_get_strength:["int collator_get_strength(Collator coll)","* Returns the current collation strength."],collator_set_attribute:["bool collator_set_attribute( Collator $coll, int $attr, int $val )","* Set collation attribute."],collator_set_strength:["bool collator_set_strength(Collator coll, int strength)","* Set the collation strength."],collator_sort:["bool collator_sort( Collator $coll, array(string) $arr [, int $sort_flags] )","* Sort array using specified collator."],collator_sort_with_sort_keys:["bool collator_sort_with_sort_keys( Collator $coll, array(string) $arr )","* Equivalent to standard PHP sort using Collator. * Uses ICU ucol_getSortKey for performance."],com_create_guid:["string com_create_guid()","Generate a globally unique identifier (GUID)"],com_event_sink:["bool com_event_sink(object comobject, object sinkobject [, mixed sinkinterface])","Connect events from a COM object to a PHP object"],com_get_active_object:["object com_get_active_object(string progid [, int code_page ])","Returns a handle to an already running instance of a COM object"],com_load_typelib:["bool com_load_typelib(string typelib_name [, int case_insensitive])","Loads a Typelibrary and registers its constants"],com_message_pump:["bool com_message_pump([int timeoutms])","Process COM messages, sleeping for up to timeoutms milliseconds"],com_print_typeinfo:["bool com_print_typeinfo(object comobject | string typelib, string dispinterface, bool wantsink)","Print out a PHP class definition for a dispatchable interface"],compact:["array compact(mixed var_names [, mixed ...])","Creates a hash containing variables and their values"],compose_locale:["static string compose_locale($array)","* Creates a locale by combining the parts of locale-ID passed * }}}"],confirm_extname_compiled:["string confirm_extname_compiled(string arg)","Return a string to confirm that the module is compiled in"],connection_aborted:["int connection_aborted(void)","Returns true if client disconnected"],connection_status:["int connection_status(void)","Returns the connection status bitfield"],constant:["mixed constant(string const_name)","Given the name of a constant this function will return the constant's associated value"],convert_cyr_string:["string convert_cyr_string(string str, string from, string to)","Convert from one Cyrillic character set to another"],convert_uudecode:["string convert_uudecode(string data)","decode a uuencoded string"],convert_uuencode:["string convert_uuencode(string data)","uuencode a string"],copy:["bool copy(string source_file, string destination_file [, resource context])","Copy a file"],cos:["float cos(float number)","Returns the cosine of the number in radians"],cosh:["float cosh(float number)","Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2"],count:["int count(mixed var [, int mode])","Count the number of elements in a variable (usually an array)"],count_chars:["mixed count_chars(string input [, int mode])","Returns info about what characters are used in input"],crc32:["string crc32(string str)","Calculate the crc32 polynomial of a string"],create_function:["string create_function(string args, string code)","Creates an anonymous function, and returns its name (funny, eh?)"],crypt:["string crypt(string str [, string salt])","Hash a string"],ctype_alnum:["bool ctype_alnum(mixed c)","Checks for alphanumeric character(s)"],ctype_alpha:["bool ctype_alpha(mixed c)","Checks for alphabetic character(s)"],ctype_cntrl:["bool ctype_cntrl(mixed c)","Checks for control character(s)"],ctype_digit:["bool ctype_digit(mixed c)","Checks for numeric character(s)"],ctype_graph:["bool ctype_graph(mixed c)","Checks for any printable character(s) except space"],ctype_lower:["bool ctype_lower(mixed c)","Checks for lowercase character(s)"],ctype_print:["bool ctype_print(mixed c)","Checks for printable character(s)"],ctype_punct:["bool ctype_punct(mixed c)","Checks for any printable character which is not whitespace or an alphanumeric character"],ctype_space:["bool ctype_space(mixed c)","Checks for whitespace character(s)"],ctype_upper:["bool ctype_upper(mixed c)","Checks for uppercase character(s)"],ctype_xdigit:["bool ctype_xdigit(mixed c)","Checks for character(s) representing a hexadecimal digit"],curl_close:["void curl_close(resource ch)","Close a cURL session"],curl_copy_handle:["resource curl_copy_handle(resource ch)","Copy a cURL handle along with all of it's preferences"],curl_errno:["int curl_errno(resource ch)","Return an integer containing the last error number"],curl_error:["string curl_error(resource ch)","Return a string contain the last error for the current session"],curl_exec:["bool curl_exec(resource ch)","Perform a cURL session"],curl_getinfo:["mixed curl_getinfo(resource ch [, int option])","Get information regarding a specific transfer"],curl_init:["resource curl_init([string url])","Initialize a cURL session"],curl_multi_add_handle:["int curl_multi_add_handle(resource mh, resource ch)","Add a normal cURL handle to a cURL multi handle"],curl_multi_close:["void curl_multi_close(resource mh)","Close a set of cURL handles"],curl_multi_exec:["int curl_multi_exec(resource mh, int &still_running)","Run the sub-connections of the current cURL handle"],curl_multi_getcontent:["string curl_multi_getcontent(resource ch)","Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set"],curl_multi_info_read:["array curl_multi_info_read(resource mh [, long msgs_in_queue])","Get information about the current transfers"],curl_multi_init:["resource curl_multi_init(void)","Returns a new cURL multi handle"],curl_multi_remove_handle:["int curl_multi_remove_handle(resource mh, resource ch)","Remove a multi handle from a set of cURL handles"],curl_multi_select:["int curl_multi_select(resource mh[, double timeout])",'Get all the sockets associated with the cURL extension, which can then be "selected"'],curl_setopt:["bool curl_setopt(resource ch, int option, mixed value)","Set an option for a cURL transfer"],curl_setopt_array:["bool curl_setopt_array(resource ch, array options)","Set an array of option for a cURL transfer"],curl_version:["array curl_version([int version])","Return cURL version information."],current:["mixed current(array array_arg)","Return the element currently pointed to by the internal array pointer"],date:["string date(string format [, long timestamp])","Format a local date/time"],date_add:["DateTime date_add(DateTime object, DateInterval interval)","Adds an interval to the current date in object."],date_create:["DateTime date_create([string time[, DateTimeZone object]])","Returns new DateTime object"],date_create_from_format:["DateTime date_create_from_format(string format, string time[, DateTimeZone object])","Returns new DateTime object formatted according to the specified format"],date_date_set:["DateTime date_date_set(DateTime object, long year, long month, long day)","Sets the date."],date_default_timezone_get:["string date_default_timezone_get()","Gets the default timezone used by all date/time functions in a script"],date_default_timezone_set:["bool date_default_timezone_set(string timezone_identifier)","Sets the default timezone used by all date/time functions in a script"],date_diff:["DateInterval date_diff(DateTime object [, bool absolute])","Returns the difference between two DateTime objects."],date_format:["string date_format(DateTime object, string format)","Returns date formatted according to given format"],date_get_last_errors:["array date_get_last_errors()","Returns the warnings and errors found while parsing a date/time string."],date_interval_create_from_date_string:["DateInterval date_interval_create_from_date_string(string time)","Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string"],date_interval_format:["string date_interval_format(DateInterval object, string format)","Formats the interval."],date_isodate_set:["DateTime date_isodate_set(DateTime object, long year, long week[, long day])","Sets the ISO date."],date_modify:["DateTime date_modify(DateTime object, string modify)","Alters the timestamp."],date_offset_get:["long date_offset_get(DateTime object)","Returns the DST offset."],date_parse:["array date_parse(string date)","Returns associative array with detailed info about given date"],date_parse_from_format:["array date_parse_from_format(string format, string date)","Returns associative array with detailed info about given date"],date_sub:["DateTime date_sub(DateTime object, DateInterval interval)","Subtracts an interval to the current date in object."],date_sun_info:["array date_sun_info(long time, float latitude, float longitude)","Returns an array with information about sun set/rise and twilight begin/end"],date_sunrise:["mixed date_sunrise(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])","Returns time of sunrise for a given day and location"],date_sunset:["mixed date_sunset(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])","Returns time of sunset for a given day and location"],date_time_set:["DateTime date_time_set(DateTime object, long hour, long minute[, long second])","Sets the time."],date_timestamp_get:["long date_timestamp_get(DateTime object)","Gets the Unix timestamp."],date_timestamp_set:["DateTime date_timestamp_set(DateTime object, long unixTimestamp)","Sets the date and time based on an Unix timestamp."],date_timezone_get:["DateTimeZone date_timezone_get(DateTime object)","Return new DateTimeZone object relative to give DateTime"],date_timezone_set:["DateTime date_timezone_set(DateTime object, DateTimeZone object)","Sets the timezone for the DateTime object."],datefmt_create:["IntlDateFormatter datefmt_create(string $locale, long date_type, long time_type[, string $timezone_str, long $calendar, string $pattern] )","* Create formatter."],datefmt_format:["string datefmt_format( [mixed]int $args or array $args )","* Format the time value as a string. }}}"],datefmt_get_calendar:["string datefmt_get_calendar( IntlDateFormatter $mf )","* Get formatter calendar."],datefmt_get_datetype:["string datefmt_get_datetype( IntlDateFormatter $mf )","* Get formatter datetype."],datefmt_get_error_code:["int datefmt_get_error_code( IntlDateFormatter $nf )","* Get formatter's last error code."],datefmt_get_error_message:["string datefmt_get_error_message( IntlDateFormatter $coll )","* Get text description for formatter's last error code."],datefmt_get_locale:["string datefmt_get_locale(IntlDateFormatter $mf)","* Get formatter locale."],datefmt_get_pattern:["string datefmt_get_pattern( IntlDateFormatter $mf )","* Get formatter pattern."],datefmt_get_timetype:["string datefmt_get_timetype( IntlDateFormatter $mf )","* Get formatter timetype."],datefmt_get_timezone_id:["string datefmt_get_timezone_id( IntlDateFormatter $mf )","* Get formatter timezone_id."],datefmt_isLenient:["string datefmt_isLenient(IntlDateFormatter $mf)","* Get formatter locale."],datefmt_localtime:["integer datefmt_localtime( IntlDateFormatter $fmt, string $text_to_parse[, int $parse_pos ])","* Parse the string $value to a localtime array }}}"],datefmt_parse:["integer datefmt_parse( IntlDateFormatter $fmt, string $text_to_parse [, int $parse_pos] )","* Parse the string $value starting at parse_pos to a Unix timestamp -int }}}"],datefmt_setLenient:["string datefmt_setLenient(IntlDateFormatter $mf)","* Set formatter lenient."],datefmt_set_calendar:["bool datefmt_set_calendar( IntlDateFormatter $mf, int $calendar )","* Set formatter calendar."],datefmt_set_pattern:["bool datefmt_set_pattern( IntlDateFormatter $mf, string $pattern )","* Set formatter pattern."],datefmt_set_timezone_id:["boolean datefmt_set_timezone_id( IntlDateFormatter $mf,$timezone_id)","* Set formatter timezone_id."],dba_close:["void dba_close(resource handle)","Closes database"],dba_delete:["bool dba_delete(string key, resource handle)","Deletes the entry associated with key If inifile: remove all other key lines"],dba_exists:["bool dba_exists(string key, resource handle)","Checks, if the specified key exists"],dba_fetch:["string dba_fetch(string key, [int skip ,] resource handle)","Fetches the data associated with key"],dba_firstkey:["string dba_firstkey(resource handle)","Resets the internal key pointer and returns the first key"],dba_handlers:["array dba_handlers([bool full_info])","List configured database handlers"],dba_insert:["bool dba_insert(string key, string value, resource handle)","If not inifile: Insert value as key, return false, if key exists already If inifile: Add vakue as key (next instance of key)"],dba_key_split:["array|false dba_key_split(string key)","Splits an inifile key into an array of the form array(0=>group,1=>value_name) but returns false if input is false or null"],dba_list:["array dba_list()","List opened databases"],dba_nextkey:["string dba_nextkey(resource handle)","Returns the next key"],dba_open:["resource dba_open(string path, string mode [, string handlername, string ...])","Opens path using the specified handler in mode"],dba_optimize:["bool dba_optimize(resource handle)","Optimizes (e.g. clean up, vacuum) database"],dba_popen:["resource dba_popen(string path, string mode [, string handlername, string ...])","Opens path using the specified handler in mode persistently"],dba_replace:["bool dba_replace(string key, string value, resource handle)","Inserts value as key, replaces key, if key exists already If inifile: remove all other key lines"],dba_sync:["bool dba_sync(resource handle)","Synchronizes database"],dcgettext:["string dcgettext(string domain_name, string msgid, long category)","Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist"],dcngettext:["string dcngettext (string domain, string msgid1, string msgid2, int n, int category)","Plural version of dcgettext()"],debug_backtrace:["array debug_backtrace([bool provide_object])","Return backtrace as array"],debug_print_backtrace:["void debug_print_backtrace(void) */","ZEND_FUNCTION(debug_print_backtrace) { zend_execute_data *ptr, *skip; int lineno; char *function_name; char *filename; char *class_name = NULL; char *call_type; char *include_filename = NULL; zval *arg_array = NULL; int indent = 0; if (zend_parse_parameters_none() == FAILURE) { return; } ptr = EG(current_execute_data);","PHP_FUNCTION(dom_document_relaxNG_validate_file) { _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); } /* }}} end dom_document_relaxNG_validate_file"],dom_document_relaxNG_validate_xml:["boolean dom_document_relaxNG_validate_xml(string source); */","PHP_FUNCTION(dom_document_relaxNG_validate_xml) { _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); } /* }}} end dom_document_relaxNG_validate_xml"],dom_document_rename_node:["DOMNode dom_document_rename_node(node n, string namespaceURI, string qualifiedName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-renameNode Since: DOM Level 3"],dom_document_save:["int dom_document_save(string file);","Convenience method to save to file"],dom_document_save_html:["string dom_document_save_html();","Convenience method to output as html"],dom_document_save_html_file:["int dom_document_save_html_file(string file);","Convenience method to save to file as html"],dom_document_savexml:["string dom_document_savexml([node n]);","URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXML Since: DOM Level 3"],dom_document_schema_validate:["boolean dom_document_schema_validate(string source); */","PHP_FUNCTION(dom_document_schema_validate_xml) { _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING); } /* }}} end dom_document_schema_validate"],dom_document_schema_validate_file:["boolean dom_document_schema_validate_file(string filename); */","PHP_FUNCTION(dom_document_schema_validate_file) { _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE); } /* }}} end dom_document_schema_validate_file"],dom_document_validate:["boolean dom_document_validate();","Since: DOM extended"],dom_document_xinclude:["int dom_document_xinclude([int options])","Substitutues xincludes in a DomDocument"],dom_domconfiguration_can_set_parameter:["boolean dom_domconfiguration_can_set_parameter(string name, domuserdata value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-canSetParameter Since:"],dom_domconfiguration_get_parameter:["domdomuserdata dom_domconfiguration_get_parameter(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-getParameter Since:"],dom_domconfiguration_set_parameter:["dom_void dom_domconfiguration_set_parameter(string name, domuserdata value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-property Since:"],dom_domerrorhandler_handle_error:["dom_boolean dom_domerrorhandler_handle_error(domerror error);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-ERRORS-DOMErrorHandler-handleError Since:"],dom_domimplementation_create_document:["DOMDocument dom_domimplementation_create_document(string namespaceURI, string qualifiedName, DOMDocumentType doctype);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocument Since: DOM Level 2"],dom_domimplementation_create_document_type:["DOMDocumentType dom_domimplementation_create_document_type(string qualifiedName, string publicId, string systemId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocType Since: DOM Level 2"],dom_domimplementation_get_feature:["DOMNode dom_domimplementation_get_feature(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementation3-getFeature Since: DOM Level 3"],dom_domimplementation_has_feature:["boolean dom_domimplementation_has_feature(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-5CED94D7 Since:"],dom_domimplementationlist_item:["domdomimplementation dom_domimplementationlist_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementationList-item Since:"],dom_domimplementationsource_get_domimplementation:["domdomimplementation dom_domimplementationsource_get_domimplementation(string features);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-getDOMImpl Since:"],dom_domimplementationsource_get_domimplementations:["domimplementationlist dom_domimplementationsource_get_domimplementations(string features);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-getDOMImpls Since:"],dom_domstringlist_item:["domstring dom_domstringlist_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMStringList-item Since:"],dom_element_get_attribute:["string dom_element_get_attribute(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9 Since:"],dom_element_get_attribute_node:["DOMAttr dom_element_get_attribute_node(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8 Since:"],dom_element_get_attribute_node_ns:["DOMAttr dom_element_get_attribute_node_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNS Since: DOM Level 2"],dom_element_get_attribute_ns:["string dom_element_get_attribute_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNS Since: DOM Level 2"],dom_element_get_elements_by_tag_name:["DOMNodeList dom_element_get_elements_by_tag_name(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918D Since:"],dom_element_get_elements_by_tag_name_ns:["DOMNodeList dom_element_get_elements_by_tag_name_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942 Since: DOM Level 2"],dom_element_has_attribute:["boolean dom_element_has_attribute(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr Since: DOM Level 2"],dom_element_has_attribute_ns:["boolean dom_element_has_attribute_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS Since: DOM Level 2"],dom_element_remove_attribute:["void dom_element_remove_attribute(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9 Since:"],dom_element_remove_attribute_node:["DOMAttr dom_element_remove_attribute_node(DOMAttr oldAttr);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198 Since:"],dom_element_remove_attribute_ns:["void dom_element_remove_attribute_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS Since: DOM Level 2"],dom_element_set_attribute:["void dom_element_set_attribute(string name, string value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082 Since:"],dom_element_set_attribute_node:["DOMAttr dom_element_set_attribute_node(DOMAttr newAttr);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154 Since:"],dom_element_set_attribute_node_ns:["DOMAttr dom_element_set_attribute_node_ns(DOMAttr newAttr);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNS Since: DOM Level 2"],dom_element_set_attribute_ns:["void dom_element_set_attribute_ns(string namespaceURI, string qualifiedName, string value);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS Since: DOM Level 2"],dom_element_set_id_attribute:["void dom_element_set_id_attribute(string name, boolean isId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttr Since: DOM Level 3"],dom_element_set_id_attribute_node:["void dom_element_set_id_attribute_node(attr idAttr, boolean isId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode Since: DOM Level 3"],dom_element_set_id_attribute_ns:["void dom_element_set_id_attribute_ns(string namespaceURI, string localName, boolean isId);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNS Since: DOM Level 3"],dom_import_simplexml:["somNode dom_import_simplexml(sxeobject node)","Get a simplexml_element object from dom to allow for processing"],dom_namednodemap_get_named_item:["DOMNode dom_namednodemap_get_named_item(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1074577549 Since:"],dom_namednodemap_get_named_item_ns:["DOMNode dom_namednodemap_get_named_item_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getNamedItemNS Since: DOM Level 2"],dom_namednodemap_item:["DOMNode dom_namednodemap_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-349467F9 Since:"],dom_namednodemap_remove_named_item:["DOMNode dom_namednodemap_remove_named_item(string name);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D58B193 Since:"],dom_namednodemap_remove_named_item_ns:["DOMNode dom_namednodemap_remove_named_item_ns(string namespaceURI, string localName);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-removeNamedItemNS Since: DOM Level 2"],dom_namednodemap_set_named_item:["DOMNode dom_namednodemap_set_named_item(DOMNode arg);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1025163788 Since:"],dom_namednodemap_set_named_item_ns:["DOMNode dom_namednodemap_set_named_item_ns(DOMNode arg);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-setNamedItemNS Since: DOM Level 2"],dom_namelist_get_name:["string dom_namelist_get_name(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#NameList-getName Since:"],dom_namelist_get_namespace_uri:["string dom_namelist_get_namespace_uri(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#NameList-getNamespaceURI Since:"],dom_node_append_child:["DomNode dom_node_append_child(DomNode newChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-184E7107 Since:"],dom_node_clone_node:["DomNode dom_node_clone_node(boolean deep);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3A0ED0A4 Since:"],dom_node_compare_document_position:["short dom_node_compare_document_position(DomNode other);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-compareDocumentPosition Since: DOM Level 3"],dom_node_get_feature:["DomNode dom_node_get_feature(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-getFeature Since: DOM Level 3"],dom_node_get_user_data:["mixed dom_node_get_user_data(string key);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-getUserData Since: DOM Level 3"],dom_node_has_attributes:["boolean dom_node_has_attributes();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeHasAttrs Since: DOM Level 2"],dom_node_has_child_nodes:["boolean dom_node_has_child_nodes();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-810594187 Since:"],dom_node_insert_before:["domnode dom_node_insert_before(DomNode newChild, DomNode refChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727 Since:"],dom_node_is_default_namespace:["boolean dom_node_is_default_namespace(string namespaceURI);","URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace Since: DOM Level 3"],dom_node_is_equal_node:["boolean dom_node_is_equal_node(DomNode arg);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isEqualNode Since: DOM Level 3"],dom_node_is_same_node:["boolean dom_node_is_same_node(DomNode other);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isSameNode Since: DOM Level 3"],dom_node_is_supported:["boolean dom_node_is_supported(string feature, string version);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Level-2-Core-Node-supports Since: DOM Level 2"],dom_node_lookup_namespace_uri:["string dom_node_lookup_namespace_uri(string prefix);","URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI Since: DOM Level 3"],dom_node_lookup_prefix:["string dom_node_lookup_prefix(string namespaceURI);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix Since: DOM Level 3"],dom_node_normalize:["void dom_node_normalize();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-normalize Since:"],dom_node_remove_child:["DomNode dom_node_remove_child(DomNode oldChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1734834066 Since:"],dom_node_replace_child:["DomNode dom_node_replace_child(DomNode newChild, DomNode oldChild);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-785887307 Since:"],dom_node_set_user_data:["mixed dom_node_set_user_data(string key, mixed data, userdatahandler handler);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-setUserData Since: DOM Level 3"],dom_nodelist_item:["DOMNode dom_nodelist_item(int index);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136 Since:"],dom_string_extend_find_offset16:["int dom_string_extend_find_offset16(int offset32);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#i18n-methods-StringExtend-findOffset16 Since:"],dom_string_extend_find_offset32:["int dom_string_extend_find_offset32(int offset16);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#i18n-methods-StringExtend-findOffset32 Since:"],dom_text_is_whitespace_in_element_content:["boolean dom_text_is_whitespace_in_element_content();","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Text3-isWhitespaceInElementContent Since: DOM Level 3"],dom_text_replace_whole_text:["DOMText dom_text_replace_whole_text(string content);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Text3-replaceWholeText Since: DOM Level 3"],dom_text_split_text:["DOMText dom_text_split_text(int offset);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-38853C1D Since:"],dom_userdatahandler_handle:["dom_void dom_userdatahandler_handle(short operation, string key, domobject data, node src, node dst);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-handleUserDataEvent Since:"],dom_xpath_evaluate:["mixed dom_xpath_evaluate(string expr [,DOMNode context]); */","PHP_FUNCTION(dom_xpath_evaluate) { php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE); } /* }}} end dom_xpath_evaluate"],dom_xpath_query:["DOMNodeList dom_xpath_query(string expr [,DOMNode context]); */","PHP_FUNCTION(dom_xpath_query) { php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY); } /* }}} end dom_xpath_query"],dom_xpath_register_ns:["boolean dom_xpath_register_ns(string prefix, string uri); */",'PHP_FUNCTION(dom_xpath_register_ns) { zval *id; xmlXPathContextPtr ctxp; int prefix_len, ns_uri_len; dom_xpath_object *intern; unsigned char *prefix, *ns_uri; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", &id, dom_xpath_class_entry, &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) { return; } intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); ctxp = (xmlXPathContextPtr) intern->ptr; if (ctxp == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Context"); RETURN_FALSE; } if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) { RETURN_FALSE } RETURN_TRUE; } /* }}}'],dom_xpath_register_php_functions:["void dom_xpath_register_php_functions() */",'PHP_FUNCTION(dom_xpath_register_php_functions) { zval *id; dom_xpath_object *intern; zval *array_value, **entry, *new_string; int name_len = 0; char *name; DOM_GET_THIS(id); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_value) == SUCCESS) { intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value)); while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) { SEPARATE_ZVAL(entry); convert_to_string_ex(entry); MAKE_STD_ZVAL(new_string); ZVAL_LONG(new_string,1); zend_hash_update(intern->registered_phpfunctions, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &new_string, sizeof(zval*), NULL); zend_hash_move_forward(Z_ARRVAL_P(array_value)); } intern->registerPhpFunctions = 2; RETURN_TRUE; } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == SUCCESS) { intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); MAKE_STD_ZVAL(new_string); ZVAL_LONG(new_string,1); zend_hash_update(intern->registered_phpfunctions, name, name_len + 1, &new_string, sizeof(zval*), NULL); intern->registerPhpFunctions = 2; } else { intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); intern->registerPhpFunctions = 1; } } /* }}} end dom_xpath_register_php_functions'],each:["array each(array arr)","Return the currently pointed key..value pair in the passed array, and advance the pointer to the next element"],easter_date:["int easter_date([int year])","Return the timestamp of midnight on Easter of a given year (defaults to current year)"],easter_days:["int easter_days([int year, [int method]])","Return the number of days after March 21 that Easter falls on for a given year (defaults to current year)"],echo:["void echo(string arg1 [, string ...])","Output one or more strings"],empty:["bool empty( mixed var )","Determine whether a variable is empty"],enchant_broker_describe:["array enchant_broker_describe(resource broker)","Enumerates the Enchant providers and tells you some rudimentary information about them. The same info is provided through phpinfo()"],enchant_broker_dict_exists:["bool enchant_broker_dict_exists(resource broker, string tag)","Wether a dictionary exists or not. Using non-empty tag"],enchant_broker_free:["boolean enchant_broker_free(resource broker)","Destroys the broker object and its dictionnaries"],enchant_broker_free_dict:["resource enchant_broker_free_dict(resource dict)","Free the dictionary resource"],enchant_broker_get_dict_path:["string enchant_broker_get_dict_path(resource broker, int dict_type)","Get the directory path for a given backend, works with ispell and myspell"],enchant_broker_get_error:["string enchant_broker_get_error(resource broker)","Returns the last error of the broker"],enchant_broker_init:["resource enchant_broker_init()","create a new broker object capable of requesting"],enchant_broker_list_dicts:["string enchant_broker_list_dicts(resource broker)","Lists the dictionaries available for the given broker"],enchant_broker_request_dict:["resource enchant_broker_request_dict(resource broker, string tag)",'create a new dictionary using tag, the non-empty language tag you wish to request a dictionary for ("en_US", "de_DE", ...)'],enchant_broker_request_pwl_dict:["resource enchant_broker_request_pwl_dict(resource broker, string filename)","creates a dictionary using a PWL file. A PWL file is personal word file one word per line. It must exist before the call."],enchant_broker_set_dict_path:["bool enchant_broker_set_dict_path(resource broker, int dict_type, string value)","Set the directory path for a given backend, works with ispell and myspell"],enchant_broker_set_ordering:["bool enchant_broker_set_ordering(resource broker, string tag, string ordering)","Declares a preference of dictionaries to use for the language described/referred to by 'tag'. The ordering is a comma delimited list of provider names. As a special exception, the \"*\" tag can be used as a language tag to declare a default ordering for any language that does not explictly declare an ordering."],enchant_dict_add_to_personal:["void enchant_dict_add_to_personal(resource dict, string word)","add 'word' to personal word list"],enchant_dict_add_to_session:["void enchant_dict_add_to_session(resource dict, string word)","add 'word' to this spell-checking session"],enchant_dict_check:["bool enchant_dict_check(resource dict, string word)","If the word is correctly spelled return true, otherwise return false"],enchant_dict_describe:["array enchant_dict_describe(resource dict)","Describes an individual dictionary 'dict'"],enchant_dict_get_error:["string enchant_dict_get_error(resource dict)","Returns the last error of the current spelling-session"],enchant_dict_is_in_session:["bool enchant_dict_is_in_session(resource dict, string word)","whether or not 'word' exists in this spelling-session"],enchant_dict_quick_check:["bool enchant_dict_quick_check(resource dict, string word [, array &suggestions])","If the word is correctly spelled return true, otherwise return false, if suggestions variable is provided, fill it with spelling alternatives."],enchant_dict_store_replacement:["void enchant_dict_store_replacement(resource dict, string mis, string cor)","add a correction for 'mis' using 'cor'. Notes that you replaced @mis with @cor, so it's possibly more likely that future occurrences of @mis will be replaced with @cor. So it might bump @cor up in the suggestion list."],enchant_dict_suggest:["array enchant_dict_suggest(resource dict, string word)","Will return a list of values if any of those pre-conditions are not met."],end:["mixed end(array array_arg)","Advances array argument's internal pointer to the last element and return it"],ereg:["int ereg(string pattern, string string [, array registers])","Regular expression match"],ereg_replace:["string ereg_replace(string pattern, string replacement, string string)","Replace regular expression"],eregi:["int eregi(string pattern, string string [, array registers])","Case-insensitive regular expression match"],eregi_replace:["string eregi_replace(string pattern, string replacement, string string)","Case insensitive replace regular expression"],error_get_last:["array error_get_last()","Get the last occurred error as associative array. Returns NULL if there hasn't been an error yet."],error_log:["bool error_log(string message [, int message_type [, string destination [, string extra_headers]]])","Send an error message somewhere"],error_reporting:["int error_reporting([int new_error_level])","Return the current error_reporting level, and if an argument was passed - change to the new level"],escapeshellarg:["string escapeshellarg(string arg)","Quote and escape an argument for use in a shell command"],escapeshellcmd:["string escapeshellcmd(string command)","Escape shell metacharacters"],exec:["string exec(string command [, array &output [, int &return_value]])","Execute an external program"],exif_imagetype:["int exif_imagetype(string imagefile)","Get the type of an image"],exif_read_data:["array exif_read_data(string filename [, sections_needed [, sub_arrays[, read_thumbnail]]])","Reads header data from the JPEG/TIFF image filename and optionally reads the internal thumbnails"],exif_tagname:["string exif_tagname(index)","Get headername for index or false if not defined"],exif_thumbnail:["string exif_thumbnail(string filename [, &width, &height [, &imagetype]])","Reads the embedded thumbnail"],exit:["void exit([mixed status])","Output a message and terminate the current script"],exp:["float exp(float number)","Returns e raised to the power of the number"],explode:["array explode(string separator, string str [, int limit])","Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned."],expm1:["float expm1(float number)","Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero"],extension_loaded:["bool extension_loaded(string extension_name)","Returns true if the named extension is loaded"],extract:["int extract(array var_array [, int extract_type [, string prefix]])","Imports variables into symbol table from an array"],ezmlm_hash:["int ezmlm_hash(string addr)","Calculate EZMLM list hash value."],fclose:["bool fclose(resource fp)","Close an open file pointer"],feof:["bool feof(resource fp)","Test for end-of-file on a file pointer"],fflush:["bool fflush(resource fp)","Flushes output"],fgetc:["string fgetc(resource fp)","Get a character from file pointer"],fgetcsv:["array fgetcsv(resource fp [,int length [, string delimiter [, string enclosure [, string escape]]]])","Get line from file pointer and parse for CSV fields"],fgets:["string fgets(resource fp[, int length])","Get a line from file pointer"],fgetss:["string fgetss(resource fp [, int length [, string allowable_tags]])","Get a line from file pointer and strip HTML tags"],file:["array file(string filename [, int flags[, resource context]])","Read entire file into an array"],file_exists:["bool file_exists(string filename)","Returns true if filename exists"],file_get_contents:["string file_get_contents(string filename [, bool use_include_path [, resource context [, long offset [, long maxlen]]]])","Read the entire file into a string"],file_put_contents:["int file_put_contents(string file, mixed data [, int flags [, resource context]])","Write/Create a file with contents data and return the number of bytes written"],fileatime:["int fileatime(string filename)","Get last access time of file"],filectime:["int filectime(string filename)","Get inode modification time of file"],filegroup:["int filegroup(string filename)","Get file group"],fileinode:["int fileinode(string filename)","Get file inode"],filemtime:["int filemtime(string filename)","Get last modification time of file"],fileowner:["int fileowner(string filename)","Get file owner"],fileperms:["int fileperms(string filename)","Get file permissions"],filesize:["int filesize(string filename)","Get file size"],filetype:["string filetype(string filename)","Get file type"],filter_has_var:["mixed filter_has_var(constant type, string variable_name)","* Returns true if the variable with the name 'name' exists in source."],filter_input:["mixed filter_input(constant type, string variable_name [, long filter [, mixed options]])","* Returns the filtered variable 'name'* from source `type`."],filter_input_array:["mixed filter_input_array(constant type, [, mixed options]])","* Returns an array with all arguments defined in 'definition'."],filter_var:["mixed filter_var(mixed variable [, long filter [, mixed options]])","* Returns the filtered version of the vriable."],filter_var_array:["mixed filter_var_array(array data, [, mixed options]])","* Returns an array with all arguments defined in 'definition'."],finfo_buffer:["string finfo_buffer(resource finfo, char *string [, int options [, resource context]])","Return infromation about a string buffer."],finfo_close:["resource finfo_close(resource finfo)","Close fileinfo resource."],finfo_file:["string finfo_file(resource finfo, char *file_name [, int options [, resource context]])","Return information about a file."],finfo_open:["resource finfo_open([int options [, string arg]])","Create a new fileinfo resource."],finfo_set_flags:["bool finfo_set_flags(resource finfo, int options)","Set libmagic configuration options."],floatval:["float floatval(mixed var)","Get the float value of a variable"],flock:["bool flock(resource fp, int operation [, int &wouldblock])","Portable file locking"],floor:["float floor(float number)","Returns the next lowest integer value from the number"],flush:["void flush(void)","Flush the output buffer"],fmod:["float fmod(float x, float y)","Returns the remainder of dividing x by y as a float"],fnmatch:["bool fnmatch(string pattern, string filename [, int flags])","Match filename against pattern"],fopen:["resource fopen(string filename, string mode [, bool use_include_path [, resource context]])","Open a file or a URL and return a file pointer"],forward_static_call:["mixed forward_static_call(mixed function_name [, mixed parmeter] [, mixed ...])","Call a user function which is the first parameter"],fpassthru:["int fpassthru(resource fp)","Output all remaining data from a file pointer"],fprintf:["int fprintf(resource stream, string format [, mixed arg1 [, mixed ...]])","Output a formatted string into a stream"],fputcsv:["int fputcsv(resource fp, array fields [, string delimiter [, string enclosure]])","Format line as CSV and write to file pointer"],fread:["string fread(resource fp, int length)","Binary-safe file read"],frenchtojd:["int frenchtojd(int month, int day, int year)","Converts a french republic calendar date to julian day count"],fscanf:["mixed fscanf(resource stream, string format [, string ...])","Implements a mostly ANSI compatible fscanf()"],fseek:["int fseek(resource fp, int offset [, int whence])","Seek on a file pointer"],fsockopen:["resource fsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]])","Open Internet or Unix domain socket connection"],fstat:["array fstat(resource fp)","Stat() on a filehandle"],ftell:["int ftell(resource fp)","Get file pointer's read/write position"],ftok:["int ftok(string pathname, string proj)","Convert a pathname and a project identifier to a System V IPC key"],ftp_alloc:["bool ftp_alloc(resource stream, int size[, &response])","Attempt to allocate space on the remote FTP server"],ftp_cdup:["bool ftp_cdup(resource stream)","Changes to the parent directory"],ftp_chdir:["bool ftp_chdir(resource stream, string directory)","Changes directories"],ftp_chmod:["int ftp_chmod(resource stream, int mode, string filename)","Sets permissions on a file"],ftp_close:["bool ftp_close(resource stream)","Closes the FTP stream"],ftp_connect:["resource ftp_connect(string host [, int port [, int timeout]])","Opens a FTP stream"],ftp_delete:["bool ftp_delete(resource stream, string file)","Deletes a file"],ftp_exec:["bool ftp_exec(resource stream, string command)","Requests execution of a program on the FTP server"],ftp_fget:["bool ftp_fget(resource stream, resource fp, string remote_file, int mode[, int resumepos])","Retrieves a file from the FTP server and writes it to an open file"],ftp_fput:["bool ftp_fput(resource stream, string remote_file, resource fp, int mode[, int startpos])","Stores a file from an open file to the FTP server"],ftp_get:["bool ftp_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos])","Retrieves a file from the FTP server and writes it to a local file"],ftp_get_option:["mixed ftp_get_option(resource stream, int option)","Gets an FTP option"],ftp_login:["bool ftp_login(resource stream, string username, string password)","Logs into the FTP server"],ftp_mdtm:["int ftp_mdtm(resource stream, string filename)","Returns the last modification time of the file, or -1 on error"],ftp_mkdir:["string ftp_mkdir(resource stream, string directory)","Creates a directory and returns the absolute path for the new directory or false on error"],ftp_nb_continue:["int ftp_nb_continue(resource stream)","Continues retrieving/sending a file nbronously"],ftp_nb_fget:["int ftp_nb_fget(resource stream, resource fp, string remote_file, int mode[, int resumepos])","Retrieves a file from the FTP server asynchronly and writes it to an open file"],ftp_nb_fput:["int ftp_nb_fput(resource stream, string remote_file, resource fp, int mode[, int startpos])","Stores a file from an open file to the FTP server nbronly"],ftp_nb_get:["int ftp_nb_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos])","Retrieves a file from the FTP server nbhronly and writes it to a local file"],ftp_nb_put:["int ftp_nb_put(resource stream, string remote_file, string local_file, int mode[, int startpos])","Stores a file on the FTP server"],ftp_nlist:["array ftp_nlist(resource stream, string directory)","Returns an array of filenames in the given directory"],ftp_pasv:["bool ftp_pasv(resource stream, bool pasv)","Turns passive mode on or off"],ftp_put:["bool ftp_put(resource stream, string remote_file, string local_file, int mode[, int startpos])","Stores a file on the FTP server"],ftp_pwd:["string ftp_pwd(resource stream)","Returns the present working directory"],ftp_raw:["array ftp_raw(resource stream, string command)","Sends a literal command to the FTP server"],ftp_rawlist:["array ftp_rawlist(resource stream, string directory [, bool recursive])","Returns a detailed listing of a directory as an array of output lines"],ftp_rename:["bool ftp_rename(resource stream, string src, string dest)","Renames the given file to a new path"],ftp_rmdir:["bool ftp_rmdir(resource stream, string directory)","Removes a directory"],ftp_set_option:["bool ftp_set_option(resource stream, int option, mixed value)","Sets an FTP option"],ftp_site:["bool ftp_site(resource stream, string cmd)","Sends a SITE command to the server"],ftp_size:["int ftp_size(resource stream, string filename)","Returns the size of the file, or -1 on error"],ftp_ssl_connect:["resource ftp_ssl_connect(string host [, int port [, int timeout]])","Opens a FTP-SSL stream"],ftp_systype:["string ftp_systype(resource stream)","Returns the system type identifier"],ftruncate:["bool ftruncate(resource fp, int size)","Truncate file to 'size' length"],func_get_arg:["mixed func_get_arg(int arg_num)","Get the $arg_num'th argument that was passed to the function"],func_get_args:["array func_get_args()","Get an array of the arguments that were passed to the function"],func_num_args:["int func_num_args(void)","Get the number of arguments that were passed to the function"],function_exists:["bool function_exists(string function_name)","Checks if the function exists"],fwrite:["int fwrite(resource fp, string str [, int length])","Binary-safe file write"],gc_collect_cycles:["int gc_collect_cycles(void)","Forces collection of any existing garbage cycles. Returns number of freed zvals"],gc_disable:["void gc_disable(void)","Deactivates the circular reference collector"],gc_enable:["void gc_enable(void)","Activates the circular reference collector"],gc_enabled:["void gc_enabled(void)","Returns status of the circular reference collector"],gd_info:["array gd_info()",""],getKeywords:["static array getKeywords(string $locale) {","* return an associative array containing keyword-value * pairs for this locale. The keys are keys to the array (doh!) * }}}"],get_browser:["mixed get_browser([string browser_name [, bool return_array]])","Get information about the capabilities of a browser. If browser_name is omitted or null, HTTP_USER_AGENT is used. Returns an object by default; if return_array is true, returns an array."],get_called_class:["string get_called_class()",'Retrieves the "Late Static Binding" class name'],get_cfg_var:["mixed get_cfg_var(string option_name)","Get the value of a PHP configuration option"],get_class:["string get_class([object object])","Retrieves the class name"],get_class_methods:["array get_class_methods(mixed class)","Returns an array of method names for class or class instance."],get_class_vars:["array get_class_vars(string class_name)","Returns an array of default properties of the class."],get_current_user:["string get_current_user(void)","Get the name of the owner of the current PHP script"],get_declared_classes:["array get_declared_classes()","Returns an array of all declared classes."],get_declared_interfaces:["array get_declared_interfaces()","Returns an array of all declared interfaces."],get_defined_constants:["array get_defined_constants([bool categorize])","Return an array containing the names and values of all defined constants"],get_defined_functions:["array get_defined_functions(void)","Returns an array of all defined functions"],get_defined_vars:["array get_defined_vars(void)","Returns an associative array of names and values of all currently defined variable names (variables in the current scope)"],get_display_language:["static string get_display_language($locale[, $in_locale = null])","* gets the language for the $locale in $in_locale or default_locale"],get_display_name:["static string get_display_name($locale[, $in_locale = null])","* gets the name for the $locale in $in_locale or default_locale"],get_display_region:["static string get_display_region($locale, $in_locale = null)","* gets the region for the $locale in $in_locale or default_locale"],get_display_script:["static string get_display_script($locale, $in_locale = null)","* gets the script for the $locale in $in_locale or default_locale"],get_extension_funcs:["array get_extension_funcs(string extension_name)","Returns an array with the names of functions belonging to the named extension"],get_headers:["array get_headers(string url[, int format])","fetches all the headers sent by the server in response to a HTTP request"],get_html_translation_table:["array get_html_translation_table([int table [, int quote_style]])","Returns the internal translation table used by htmlspecialchars and htmlentities"],get_include_path:["string get_include_path()","Get the current include_path configuration option"],get_included_files:["array get_included_files(void)","Returns an array with the file names that were include_once()'d"],get_loaded_extensions:["array get_loaded_extensions([bool zend_extensions])","Return an array containing names of loaded extensions"],get_magic_quotes_gpc:["int get_magic_quotes_gpc(void)","Get the current active configuration setting of magic_quotes_gpc"],get_magic_quotes_runtime:["int get_magic_quotes_runtime(void)","Get the current active configuration setting of magic_quotes_runtime"],get_meta_tags:["array get_meta_tags(string filename [, bool use_include_path])","Extracts all meta tag content attributes from a file and returns an array"],get_object_vars:["array get_object_vars(object obj)","Returns an array of object properties"],get_parent_class:["string get_parent_class([mixed object])","Retrieves the parent class name for object or class or current scope."],get_resource_type:["string get_resource_type(resource res)","Get the resource type name for a given resource"],getallheaders:["array getallheaders(void)",""],getcwd:["mixed getcwd(void)","Gets the current directory"],getdate:["array getdate([int timestamp])","Get date/time information"],getenv:["string getenv(string varname)","Get the value of an environment variable"],gethostbyaddr:["string gethostbyaddr(string ip_address)","Get the Internet host name corresponding to a given IP address"],gethostbyname:["string gethostbyname(string hostname)","Get the IP address corresponding to a given Internet host name"],gethostbynamel:["array gethostbynamel(string hostname)","Return a list of IP addresses that a given hostname resolves to."],gethostname:["string gethostname()","Get the host name of the current machine"],getimagesize:["array getimagesize(string imagefile [, array info])","Get the size of an image as 4-element array"],getlastmod:["int getlastmod(void)","Get time of last page modification"],getmygid:["int getmygid(void)","Get PHP script owner's GID"],getmyinode:["int getmyinode(void)","Get the inode of the current script being parsed"],getmypid:["int getmypid(void)","Get current process ID"],getmyuid:["int getmyuid(void)","Get PHP script owner's UID"],getopt:["array getopt(string options [, array longopts])","Get options from the command line argument list"],getprotobyname:["int getprotobyname(string name)","Returns protocol number associated with name as per /etc/protocols"],getprotobynumber:["string getprotobynumber(int proto)","Returns protocol name associated with protocol number proto"],getrandmax:["int getrandmax(void)","Returns the maximum value a random number can have"],getrusage:["array getrusage([int who])","Returns an array of usage statistics"],getservbyname:["int getservbyname(string service, string protocol)",'Returns port associated with service. Protocol must be "tcp" or "udp"'],getservbyport:["string getservbyport(int port, string protocol)",'Returns service name associated with port. Protocol must be "tcp" or "udp"'],gettext:["string gettext(string msgid)","Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist"],gettimeofday:["array gettimeofday([bool get_as_float])","Returns the current time as array"],gettype:["string gettype(mixed var)","Returns the type of the variable"],glob:["array glob(string pattern [, int flags])","Find pathnames matching a pattern"],gmdate:["string gmdate(string format [, long timestamp])","Format a GMT date/time"],gmmktime:["int gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])","Get UNIX timestamp for a GMT date"],gmp_abs:["resource gmp_abs(resource a)","Calculates absolute value"],gmp_add:["resource gmp_add(resource a, resource b)","Add a and b"],gmp_and:["resource gmp_and(resource a, resource b)","Calculates logical AND of a and b"],gmp_clrbit:["void gmp_clrbit(resource &a, int index)","Clears bit in a"],gmp_cmp:["int gmp_cmp(resource a, resource b)","Compares two numbers"],gmp_com:["resource gmp_com(resource a)","Calculates one's complement of a"],gmp_div_q:["resource gmp_div_q(resource a, resource b [, int round])","Divide a by b, returns quotient only"],gmp_div_qr:["array gmp_div_qr(resource a, resource b [, int round])","Divide a by b, returns quotient and reminder"],gmp_div_r:["resource gmp_div_r(resource a, resource b [, int round])","Divide a by b, returns reminder only"],gmp_divexact:["resource gmp_divexact(resource a, resource b)","Divide a by b using exact division algorithm"],gmp_fact:["resource gmp_fact(int a)","Calculates factorial function"],gmp_gcd:["resource gmp_gcd(resource a, resource b)","Computes greatest common denominator (gcd) of a and b"],gmp_gcdext:["array gmp_gcdext(resource a, resource b)","Computes G, S, and T, such that AS + BT = G = `gcd' (A, B)"],gmp_hamdist:["int gmp_hamdist(resource a, resource b)","Calculates hamming distance between a and b"],gmp_init:["resource gmp_init(mixed number [, int base])","Initializes GMP number"],gmp_intval:["int gmp_intval(resource gmpnumber)","Gets signed long value of GMP number"],gmp_invert:["resource gmp_invert(resource a, resource b)","Computes the inverse of a modulo b"],gmp_jacobi:["int gmp_jacobi(resource a, resource b)","Computes Jacobi symbol"],gmp_legendre:["int gmp_legendre(resource a, resource b)","Computes Legendre symbol"],gmp_mod:["resource gmp_mod(resource a, resource b)","Computes a modulo b"],gmp_mul:["resource gmp_mul(resource a, resource b)","Multiply a and b"],gmp_neg:["resource gmp_neg(resource a)","Negates a number"],gmp_nextprime:["resource gmp_nextprime(resource a)","Finds next prime of a"],gmp_or:["resource gmp_or(resource a, resource b)","Calculates logical OR of a and b"],gmp_perfect_square:["bool gmp_perfect_square(resource a)","Checks if a is an exact square"],gmp_popcount:["int gmp_popcount(resource a)","Calculates the population count of a"],gmp_pow:["resource gmp_pow(resource base, int exp)","Raise base to power exp"],gmp_powm:["resource gmp_powm(resource base, resource exp, resource mod)","Raise base to power exp and take result modulo mod"],gmp_prob_prime:["int gmp_prob_prime(resource a[, int reps])",'Checks if a is "probably prime"'],gmp_random:["resource gmp_random([int limiter])","Gets random number"],gmp_scan0:["int gmp_scan0(resource a, int start)","Finds first zero bit"],gmp_scan1:["int gmp_scan1(resource a, int start)","Finds first non-zero bit"],gmp_setbit:["void gmp_setbit(resource &a, int index[, bool set_clear])","Sets or clear bit in a"],gmp_sign:["int gmp_sign(resource a)","Gets the sign of the number"],gmp_sqrt:["resource gmp_sqrt(resource a)","Takes integer part of square root of a"],gmp_sqrtrem:["array gmp_sqrtrem(resource a)","Square root with remainder"],gmp_strval:["string gmp_strval(resource gmpnumber [, int base])","Gets string representation of GMP number"],gmp_sub:["resource gmp_sub(resource a, resource b)","Subtract b from a"],gmp_testbit:["bool gmp_testbit(resource a, int index)","Tests if bit is set in a"],gmp_xor:["resource gmp_xor(resource a, resource b)","Calculates logical exclusive OR of a and b"],gmstrftime:["string gmstrftime(string format [, int timestamp])","Format a GMT/UCT time/date according to locale settings"],grapheme_extract:["string grapheme_extract(string str, int size[, int extract_type[, int start[, int next]]])","Function to extract a sequence of default grapheme clusters"],grapheme_stripos:["int grapheme_stripos(string haystack, string needle [, int offset ])","Find position of first occurrence of a string within another, ignoring case differences"],grapheme_stristr:["string grapheme_stristr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another"],grapheme_strlen:["int grapheme_strlen(string str)","Get number of graphemes in a string"],grapheme_strpos:["int grapheme_strpos(string haystack, string needle [, int offset ])","Find position of first occurrence of a string within another"],grapheme_strripos:["int grapheme_strripos(string haystack, string needle [, int offset])","Find position of last occurrence of a string within another, ignoring case"],grapheme_strrpos:["int grapheme_strrpos(string haystack, string needle [, int offset])","Find position of last occurrence of a string within another"],grapheme_strstr:["string grapheme_strstr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another"],grapheme_substr:["string grapheme_substr(string str, int start [, int length])","Returns part of a string"],gregoriantojd:["int gregoriantojd(int month, int day, int year)","Converts a gregorian calendar date to julian day count"],gzcompress:["string gzcompress(string data [, int level])","Gzip-compress a string"],gzdeflate:["string gzdeflate(string data [, int level])","Gzip-compress a string"],gzencode:["string gzencode(string data [, int level [, int encoding_mode]])","GZ encode a string"],gzfile:["array gzfile(string filename [, int use_include_path])","Read und uncompress entire .gz-file into an array"],gzinflate:["string gzinflate(string data [, int length])","Unzip a gzip-compressed string"],gzopen:["resource gzopen(string filename, string mode [, int use_include_path])","Open a .gz-file and return a .gz-file pointer"],gzuncompress:["string gzuncompress(string data [, int length])","Unzip a gzip-compressed string"],hash:["string hash(string algo, string data[, bool raw_output = false])","Generate a hash of a given input string Returns lowercase hexits by default"],hash_algos:["array hash_algos(void)","Return a list of registered hashing algorithms"],hash_copy:["resource hash_copy(resource context)","Copy hash resource"],hash_file:["string hash_file(string algo, string filename[, bool raw_output = false])","Generate a hash of a given file Returns lowercase hexits by default"],hash_final:["string hash_final(resource context[, bool raw_output=false])","Output resulting digest"],hash_hmac:["string hash_hmac(string algo, string data, string key[, bool raw_output = false])","Generate a hash of a given input string with a key using HMAC Returns lowercase hexits by default"],hash_hmac_file:["string hash_hmac_file(string algo, string filename, string key[, bool raw_output = false])","Generate a hash of a given file with a key using HMAC Returns lowercase hexits by default"],hash_init:["resource hash_init(string algo[, int options, string key])","Initialize a hashing context"],hash_update:["bool hash_update(resource context, string data)","Pump data into the hashing algorithm"],hash_update_file:["bool hash_update_file(resource context, string filename[, resource context])","Pump data into the hashing algorithm from a file"],hash_update_stream:["int hash_update_stream(resource context, resource handle[, integer length])","Pump data into the hashing algorithm from an open stream"],header:["void header(string header [, bool replace, [int http_response_code]])","Sends a raw HTTP header"],header_remove:["void header_remove([string name])","Removes an HTTP header previously set using header()"],headers_list:["array headers_list(void)","Return list of headers to be sent / already sent"],headers_sent:["bool headers_sent([string &$file [, int &$line]])","Returns true if headers have already been sent, false otherwise"],hebrev:["string hebrev(string str [, int max_chars_per_line])","Converts logical Hebrew text to visual text"],hebrevc:["string hebrevc(string str [, int max_chars_per_line])","Converts logical Hebrew text to visual text with newline conversion"],hexdec:["int hexdec(string hexadecimal_number)","Returns the decimal equivalent of the hexadecimal number"],highlight_file:["bool highlight_file(string file_name [, bool return] )","Syntax highlight a source file"],highlight_string:["bool highlight_string(string string [, bool return] )","Syntax highlight a string or optionally return it"],html_entity_decode:["string html_entity_decode(string string [, int quote_style][, string charset])","Convert all HTML entities to their applicable characters"],htmlentities:["string htmlentities(string string [, int quote_style[, string charset[, bool double_encode]]])","Convert all applicable characters to HTML entities"],htmlspecialchars:["string htmlspecialchars(string string [, int quote_style[, string charset[, bool double_encode]]])","Convert special characters to HTML entities"],htmlspecialchars_decode:["string htmlspecialchars_decode(string string [, int quote_style])","Convert special HTML entities back to characters"],http_build_query:["string http_build_query(mixed formdata [, string prefix [, string arg_separator]])","Generates a form-encoded query string from an associative array or object."],hypot:["float hypot(float num1, float num2)","Returns sqrt(num1*num1 + num2*num2)"],ibase_add_user:["bool ibase_add_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]])","Add a user to security database"],ibase_affected_rows:["int ibase_affected_rows( [ resource link_identifier ] )","Returns the number of rows affected by the previous INSERT, UPDATE or DELETE statement"],ibase_backup:["mixed ibase_backup(resource service_handle, string source_db, string dest_file [, int options [, bool verbose]])","Initiates a backup task in the service manager and returns immediately"],ibase_blob_add:["bool ibase_blob_add(resource blob_handle, string data)","Add data into created blob"],ibase_blob_cancel:["bool ibase_blob_cancel(resource blob_handle)","Cancel creating blob"],ibase_blob_close:["string ibase_blob_close(resource blob_handle)","Close blob"],ibase_blob_create:["resource ibase_blob_create([resource link_identifier])","Create blob for adding data"],ibase_blob_echo:["bool ibase_blob_echo([ resource link_identifier, ] string blob_id)","Output blob contents to browser"],ibase_blob_get:["string ibase_blob_get(resource blob_handle, int len)","Get len bytes data from open blob"],ibase_blob_import:["string ibase_blob_import([ resource link_identifier, ] resource file)","Create blob, copy file in it, and close it"],ibase_blob_info:["array ibase_blob_info([ resource link_identifier, ] string blob_id)","Return blob length and other useful info"],ibase_blob_open:["resource ibase_blob_open([ resource link_identifier, ] string blob_id)","Open blob for retrieving data parts"],ibase_close:["bool ibase_close([resource link_identifier])","Close an InterBase connection"],ibase_commit:["bool ibase_commit( resource link_identifier )","Commit transaction"],ibase_commit_ret:["bool ibase_commit_ret( resource link_identifier )","Commit transaction and retain the transaction context"],ibase_connect:["resource ibase_connect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])","Open a connection to an InterBase database"],ibase_db_info:["string ibase_db_info(resource service_handle, string db, int action [, int argument])","Request statistics about a database"],ibase_delete_user:["bool ibase_delete_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]])","Delete a user from security database"],ibase_drop_db:["bool ibase_drop_db([resource link_identifier])","Drop an InterBase database"],ibase_errcode:["int ibase_errcode(void)","Return error code"],ibase_errmsg:["string ibase_errmsg(void)","Return error message"],ibase_execute:["mixed ibase_execute(resource query [, mixed bind_arg [, mixed bind_arg [, ...]]])","Execute a previously prepared query"],ibase_fetch_assoc:["array ibase_fetch_assoc(resource result [, int fetch_flags])","Fetch a row from the results of a query"],ibase_fetch_object:["object ibase_fetch_object(resource result [, int fetch_flags])","Fetch a object from the results of a query"],ibase_fetch_row:["array ibase_fetch_row(resource result [, int fetch_flags])","Fetch a row from the results of a query"],ibase_field_info:["array ibase_field_info(resource query_result, int field_number)","Get information about a field"],ibase_free_event_handler:["bool ibase_free_event_handler(resource event)","Frees the event handler set by ibase_set_event_handler()"],ibase_free_query:["bool ibase_free_query(resource query)","Free memory used by a query"],ibase_free_result:["bool ibase_free_result(resource result)","Free the memory used by a result"],ibase_gen_id:["int ibase_gen_id(string generator [, int increment [, resource link_identifier ]])","Increments the named generator and returns its new value"],ibase_maintain_db:["bool ibase_maintain_db(resource service_handle, string db, int action [, int argument])","Execute a maintenance command on the database server"],ibase_modify_user:["bool ibase_modify_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]])","Modify a user in security database"],ibase_name_result:["bool ibase_name_result(resource result, string name)","Assign a name to a result for use with ... WHERE CURRENT OF statements"],ibase_num_fields:["int ibase_num_fields(resource query_result)","Get the number of fields in result"],ibase_num_params:["int ibase_num_params(resource query)","Get the number of params in a prepared query"],ibase_num_rows:["int ibase_num_rows( resource result_identifier )","Return the number of rows that are available in a result"],ibase_param_info:["array ibase_param_info(resource query, int field_number)","Get information about a parameter"],ibase_pconnect:["resource ibase_pconnect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])","Open a persistent connection to an InterBase database"],ibase_prepare:["resource ibase_prepare(resource link_identifier[, string query [, resource trans_identifier ]])","Prepare a query for later execution"],ibase_query:["mixed ibase_query([resource link_identifier, [ resource link_identifier, ]] string query [, mixed bind_arg [, mixed bind_arg [, ...]]])","Execute a query"],ibase_restore:["mixed ibase_restore(resource service_handle, string source_file, string dest_db [, int options [, bool verbose]])","Initiates a restore task in the service manager and returns immediately"],ibase_rollback:["bool ibase_rollback( resource link_identifier )","Rollback transaction"],ibase_rollback_ret:["bool ibase_rollback_ret( resource link_identifier )","Rollback transaction and retain the transaction context"],ibase_server_info:["string ibase_server_info(resource service_handle, int action)","Request information about a database server"],ibase_service_attach:["resource ibase_service_attach(string host, string dba_username, string dba_password)","Connect to the service manager"],ibase_service_detach:["bool ibase_service_detach(resource service_handle)","Disconnect from the service manager"],ibase_set_event_handler:["resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]])","Register the callback for handling each of the named events"],ibase_trans:["resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]])","Start a transaction over one or several databases"],ibase_wait_event:["string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]])","Waits for any one of the passed Interbase events to be posted by the database, and returns its name"],iconv:["string iconv(string in_charset, string out_charset, string str)","Returns str converted to the out_charset character set"],iconv_get_encoding:["mixed iconv_get_encoding([string type])","Get internal encoding and output encoding for ob_iconv_handler()"],iconv_mime_decode:["string iconv_mime_decode(string encoded_string [, int mode, string charset])","Decodes a mime header field"],iconv_mime_decode_headers:["array iconv_mime_decode_headers(string headers [, int mode, string charset])","Decodes multiple mime header fields"],iconv_mime_encode:["string iconv_mime_encode(string field_name, string field_value [, array preference])","Composes a mime header field with field_name and field_value in a specified scheme"],iconv_set_encoding:["bool iconv_set_encoding(string type, string charset)","Sets internal encoding and output encoding for ob_iconv_handler()"],iconv_strlen:["int iconv_strlen(string str [, string charset])","Returns the character count of str"],iconv_strpos:["int iconv_strpos(string haystack, string needle [, int offset [, string charset]])","Finds position of first occurrence of needle within part of haystack beginning with offset"],iconv_strrpos:["int iconv_strrpos(string haystack, string needle [, string charset])","Finds position of last occurrence of needle within part of haystack beginning with offset"],iconv_substr:["string iconv_substr(string str, int offset, [int length, string charset])","Returns specified part of a string"],idate:["int idate(string format [, int timestamp])","Format a local time/date as integer"],idn_to_ascii:["int idn_to_ascii(string domain[, int options])","Converts an Unicode domain to ASCII representation, as defined in the IDNA RFC"],idn_to_utf8:["int idn_to_utf8(string domain[, int options])","Converts an ASCII representation of the domain to Unicode (UTF-8), as defined in the IDNA RFC"],ignore_user_abort:["int ignore_user_abort([string value])","Set whether we want to ignore a user abort event or not"],image2wbmp:["bool image2wbmp(resource im [, string filename [, int threshold]])","Output WBMP image to browser or file"],image_type_to_extension:["string image_type_to_extension(int imagetype [, bool include_dot])","Get file extension for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype"],image_type_to_mime_type:["string image_type_to_mime_type(int imagetype)","Get Mime-Type for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype"],imagealphablending:["bool imagealphablending(resource im, bool on)","Turn alpha blending mode on or off for the given image"],imageantialias:["bool imageantialias(resource im, bool on)","Should antialiased functions used or not"],imagearc:["bool imagearc(resource im, int cx, int cy, int w, int h, int s, int e, int col)","Draw a partial ellipse"],imagechar:["bool imagechar(resource im, int font, int x, int y, string c, int col)","Draw a character"],imagecharup:["bool imagecharup(resource im, int font, int x, int y, string c, int col)","Draw a character rotated 90 degrees counter-clockwise"],imagecolorallocate:["int imagecolorallocate(resource im, int red, int green, int blue)","Allocate a color for an image"],imagecolorallocatealpha:["int imagecolorallocatealpha(resource im, int red, int green, int blue, int alpha)","Allocate a color with an alpha level. Works for true color and palette based images"],imagecolorat:["int imagecolorat(resource im, int x, int y)","Get the index of the color of a pixel"],imagecolorclosest:["int imagecolorclosest(resource im, int red, int green, int blue)","Get the index of the closest color to the specified color"],imagecolorclosestalpha:["int imagecolorclosestalpha(resource im, int red, int green, int blue, int alpha)","Find the closest matching colour with alpha transparency"],imagecolorclosesthwb:["int imagecolorclosesthwb(resource im, int red, int green, int blue)","Get the index of the color which has the hue, white and blackness nearest to the given color"],imagecolordeallocate:["bool imagecolordeallocate(resource im, int index)","De-allocate a color for an image"],imagecolorexact:["int imagecolorexact(resource im, int red, int green, int blue)","Get the index of the specified color"],imagecolorexactalpha:["int imagecolorexactalpha(resource im, int red, int green, int blue, int alpha)","Find exact match for colour with transparency"],imagecolormatch:["bool imagecolormatch(resource im1, resource im2)","Makes the colors of the palette version of an image more closely match the true color version"],imagecolorresolve:["int imagecolorresolve(resource im, int red, int green, int blue)","Get the index of the specified color or its closest possible alternative"],imagecolorresolvealpha:["int imagecolorresolvealpha(resource im, int red, int green, int blue, int alpha)","Resolve/Allocate a colour with an alpha level. Works for true colour and palette based images"],imagecolorset:["void imagecolorset(resource im, int col, int red, int green, int blue)","Set the color for the specified palette index"],imagecolorsforindex:["array imagecolorsforindex(resource im, int col)","Get the colors for an index"],imagecolorstotal:["int imagecolorstotal(resource im)","Find out the number of colors in an image's palette"],imagecolortransparent:["int imagecolortransparent(resource im [, int col])","Define a color as transparent"],imageconvolution:["resource imageconvolution(resource src_im, array matrix3x3, double div, double offset)","Apply a 3x3 convolution matrix, using coefficient div and offset"],imagecopy:["bool imagecopy(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h)","Copy part of an image"],imagecopymerge:["bool imagecopymerge(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct)","Merge one part of an image with another"],imagecopymergegray:["bool imagecopymergegray(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct)","Merge one part of an image with another"],imagecopyresampled:["bool imagecopyresampled(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h)","Copy and resize part of an image using resampling to help ensure clarity"],imagecopyresized:["bool imagecopyresized(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h)","Copy and resize part of an image"],imagecreate:["resource imagecreate(int x_size, int y_size)","Create a new image"],imagecreatefromgd:["resource imagecreatefromgd(string filename)","Create a new image from GD file or URL"],imagecreatefromgd2:["resource imagecreatefromgd2(string filename)","Create a new image from GD2 file or URL"],imagecreatefromgd2part:["resource imagecreatefromgd2part(string filename, int srcX, int srcY, int width, int height)","Create a new image from a given part of GD2 file or URL"],imagecreatefromgif:["resource imagecreatefromgif(string filename)","Create a new image from GIF file or URL"],imagecreatefromjpeg:["resource imagecreatefromjpeg(string filename)","Create a new image from JPEG file or URL"],imagecreatefrompng:["resource imagecreatefrompng(string filename)","Create a new image from PNG file or URL"],imagecreatefromstring:["resource imagecreatefromstring(string image)","Create a new image from the image stream in the string"],imagecreatefromwbmp:["resource imagecreatefromwbmp(string filename)","Create a new image from WBMP file or URL"],imagecreatefromxbm:["resource imagecreatefromxbm(string filename)","Create a new image from XBM file or URL"],imagecreatefromxpm:["resource imagecreatefromxpm(string filename)","Create a new image from XPM file or URL"],imagecreatetruecolor:["resource imagecreatetruecolor(int x_size, int y_size)","Create a new true color image"],imagedashedline:["bool imagedashedline(resource im, int x1, int y1, int x2, int y2, int col)","Draw a dashed line"],imagedestroy:["bool imagedestroy(resource im)","Destroy an image"],imageellipse:["bool imageellipse(resource im, int cx, int cy, int w, int h, int color)","Draw an ellipse"],imagefill:["bool imagefill(resource im, int x, int y, int col)","Flood fill"],imagefilledarc:["bool imagefilledarc(resource im, int cx, int cy, int w, int h, int s, int e, int col, int style)","Draw a filled partial ellipse"],imagefilledellipse:["bool imagefilledellipse(resource im, int cx, int cy, int w, int h, int color)","Draw an ellipse"],imagefilledpolygon:["bool imagefilledpolygon(resource im, array point, int num_points, int col)","Draw a filled polygon"],imagefilledrectangle:["bool imagefilledrectangle(resource im, int x1, int y1, int x2, int y2, int col)","Draw a filled rectangle"],imagefilltoborder:["bool imagefilltoborder(resource im, int x, int y, int border, int col)","Flood fill to specific color"],imagefilter:["bool imagefilter(resource src_im, int filtertype, [args] )","Applies Filter an image using a custom angle"],imagefontheight:["int imagefontheight(int font)","Get font height"],imagefontwidth:["int imagefontwidth(int font)","Get font width"],imageftbbox:["array imageftbbox(float size, float angle, string font_file, string text [, array extrainfo])","Give the bounding box of a text using fonts via freetype2"],imagefttext:["array imagefttext(resource im, float size, float angle, int x, int y, int col, string font_file, string text [, array extrainfo])","Write text to the image using fonts via freetype2"],imagegammacorrect:["bool imagegammacorrect(resource im, float inputgamma, float outputgamma)","Apply a gamma correction to a GD image"],imagegd:["bool imagegd(resource im [, string filename])","Output GD image to browser or file"],imagegd2:["bool imagegd2(resource im [, string filename, [, int chunk_size, [, int type]]])","Output GD2 image to browser or file"],imagegif:["bool imagegif(resource im [, string filename])","Output GIF image to browser or file"],imagegrabscreen:["resource imagegrabscreen()","Grab a screenshot"],imagegrabwindow:["resource imagegrabwindow(int window_handle [, int client_area])","Grab a window or its client area using a windows handle (HWND property in COM instance)"],imageinterlace:["int imageinterlace(resource im [, int interlace])","Enable or disable interlace"],imageistruecolor:["bool imageistruecolor(resource im)","return true if the image uses truecolor"],imagejpeg:["bool imagejpeg(resource im [, string filename [, int quality]])","Output JPEG image to browser or file"],imagelayereffect:["bool imagelayereffect(resource im, int effect)","Set the alpha blending flag to use the bundled libgd layering effects"],imageline:["bool imageline(resource im, int x1, int y1, int x2, int y2, int col)","Draw a line"],imageloadfont:["int imageloadfont(string filename)","Load a new font"],imagepalettecopy:["void imagepalettecopy(resource dst, resource src)","Copy the palette from the src image onto the dst image"],imagepng:["bool imagepng(resource im [, string filename])","Output PNG image to browser or file"],imagepolygon:["bool imagepolygon(resource im, array point, int num_points, int col)","Draw a polygon"],imagepsbbox:["array imagepsbbox(string text, resource font, int size [, int space, int tightness, float angle])","Return the bounding box needed by a string if rasterized"],imagepscopyfont:["int imagepscopyfont(int font_index)","Make a copy of a font for purposes like extending or reenconding"],imagepsencodefont:["bool imagepsencodefont(resource font_index, string filename)","To change a fonts character encoding vector"],imagepsextendfont:["bool imagepsextendfont(resource font_index, float extend)","Extend or or condense (if extend < 1) a font"],imagepsfreefont:["bool imagepsfreefont(resource font_index)","Free memory used by a font"],imagepsloadfont:["resource imagepsloadfont(string pathname)","Load a new font from specified file"],imagepsslantfont:["bool imagepsslantfont(resource font_index, float slant)","Slant a font"],imagepstext:["array imagepstext(resource image, string text, resource font, int size, int foreground, int background, int xcoord, int ycoord [, int space [, int tightness [, float angle [, int antialias])","Rasterize a string over an image"],imagerectangle:["bool imagerectangle(resource im, int x1, int y1, int x2, int y2, int col)","Draw a rectangle"],imagerotate:["resource imagerotate(resource src_im, float angle, int bgdcolor [, int ignoretransparent])","Rotate an image using a custom angle"],imagesavealpha:["bool imagesavealpha(resource im, bool on)","Include alpha channel to a saved image"],imagesetbrush:["bool imagesetbrush(resource image, resource brush)",'Set the brush image to $brush when filling $image with the "IMG_COLOR_BRUSHED" color'],imagesetpixel:["bool imagesetpixel(resource im, int x, int y, int col)","Set a single pixel"],imagesetstyle:["bool imagesetstyle(resource im, array styles)","Set the line drawing styles for use with imageline and IMG_COLOR_STYLED."],imagesetthickness:["bool imagesetthickness(resource im, int thickness)","Set line thickness for drawing lines, ellipses, rectangles, polygons etc."],imagesettile:["bool imagesettile(resource image, resource tile)",'Set the tile image to $tile when filling $image with the "IMG_COLOR_TILED" color'],imagestring:["bool imagestring(resource im, int font, int x, int y, string str, int col)","Draw a string horizontally"],imagestringup:["bool imagestringup(resource im, int font, int x, int y, string str, int col)","Draw a string vertically - rotated 90 degrees counter-clockwise"],imagesx:["int imagesx(resource im)","Get image width"],imagesy:["int imagesy(resource im)","Get image height"],imagetruecolortopalette:["void imagetruecolortopalette(resource im, bool ditherFlag, int colorsWanted)","Convert a true colour image to a palette based image with a number of colours, optionally using dithering."],imagettfbbox:["array imagettfbbox(float size, float angle, string font_file, string text)","Give the bounding box of a text using TrueType fonts"],imagettftext:["array imagettftext(resource im, float size, float angle, int x, int y, int col, string font_file, string text)","Write text to the image using a TrueType font"],imagetypes:["int imagetypes(void)","Return the types of images supported in a bitfield - 1=GIF, 2=JPEG, 4=PNG, 8=WBMP, 16=XPM"],imagewbmp:["bool imagewbmp(resource im [, string filename, [, int foreground]])","Output WBMP image to browser or file"],imagexbm:["int imagexbm(int im, string filename [, int foreground])","Output XBM image to browser or file"],imap_8bit:["string imap_8bit(string text)","Convert an 8-bit string to a quoted-printable string"],imap_alerts:["array imap_alerts(void)","Returns an array of all IMAP alerts that have been generated since the last page load or since the last imap_alerts() call, whichever came last. The alert stack is cleared after imap_alerts() is called."],imap_append:["bool imap_append(resource stream_id, string folder, string message [, string options [, string internal_date]])","Append a new message to a specified mailbox"],imap_base64:["string imap_base64(string text)","Decode BASE64 encoded text"],imap_binary:["string imap_binary(string text)","Convert an 8bit string to a base64 string"],imap_body:["string imap_body(resource stream_id, int msg_no [, int options])","Read the message body"],imap_bodystruct:["object imap_bodystruct(resource stream_id, int msg_no, string section)","Read the structure of a specified body section of a specific message"],imap_check:["object imap_check(resource stream_id)","Get mailbox properties"],imap_clearflag_full:["bool imap_clearflag_full(resource stream_id, string sequence, string flag [, int options])","Clears flags on messages"],imap_close:["bool imap_close(resource stream_id [, int options])","Close an IMAP stream"],imap_createmailbox:["bool imap_createmailbox(resource stream_id, string mailbox)","Create a new mailbox"],imap_delete:["bool imap_delete(resource stream_id, int msg_no [, int options])","Mark a message for deletion"],imap_deletemailbox:["bool imap_deletemailbox(resource stream_id, string mailbox)","Delete a mailbox"],imap_errors:["array imap_errors(void)","Returns an array of all IMAP errors generated since the last page load, or since the last imap_errors() call, whichever came last. The error stack is cleared after imap_errors() is called."],imap_expunge:["bool imap_expunge(resource stream_id)","Permanently delete all messages marked for deletion"],imap_fetch_overview:["array imap_fetch_overview(resource stream_id, string sequence [, int options])","Read an overview of the information in the headers of the given message sequence"],imap_fetchbody:["string imap_fetchbody(resource stream_id, int msg_no, string section [, int options])","Get a specific body section"],imap_fetchheader:["string imap_fetchheader(resource stream_id, int msg_no [, int options])","Get the full unfiltered header for a message"],imap_fetchstructure:["object imap_fetchstructure(resource stream_id, int msg_no [, int options])","Read the full structure of a message"],imap_gc:["bool imap_gc(resource stream_id, int flags)","This function garbage collects (purges) the cache of entries of a specific type."],imap_get_quota:["array imap_get_quota(resource stream_id, string qroot)","Returns the quota set to the mailbox account qroot"],imap_get_quotaroot:["array imap_get_quotaroot(resource stream_id, string mbox)","Returns the quota set to the mailbox account mbox"],imap_getacl:["array imap_getacl(resource stream_id, string mailbox)","Gets the ACL for a given mailbox"],imap_getmailboxes:["array imap_getmailboxes(resource stream_id, string ref, string pattern)","Reads the list of mailboxes and returns a full array of objects containing name, attributes, and delimiter"],imap_getsubscribed:["array imap_getsubscribed(resource stream_id, string ref, string pattern)","Return a list of subscribed mailboxes, in the same format as imap_getmailboxes()"],imap_headerinfo:["object imap_headerinfo(resource stream_id, int msg_no [, int from_length [, int subject_length [, string default_host]]])","Read the headers of the message"],imap_headers:["array imap_headers(resource stream_id)","Returns headers for all messages in a mailbox"],imap_last_error:["string imap_last_error(void)","Returns the last error that was generated by an IMAP function. The error stack is NOT cleared after this call."],imap_list:["array imap_list(resource stream_id, string ref, string pattern)","Read the list of mailboxes"],imap_listscan:["array imap_listscan(resource stream_id, string ref, string pattern, string content)","Read list of mailboxes containing a certain string"],imap_lsub:["array imap_lsub(resource stream_id, string ref, string pattern)","Return a list of subscribed mailboxes"],imap_mail:["bool imap_mail(string to, string subject, string message [, string additional_headers [, string cc [, string bcc [, string rpath]]]])","Send an email message"],imap_mail_compose:["string imap_mail_compose(array envelope, array body)","Create a MIME message based on given envelope and body sections"],imap_mail_copy:["bool imap_mail_copy(resource stream_id, string msglist, string mailbox [, int options])","Copy specified message to a mailbox"],imap_mail_move:["bool imap_mail_move(resource stream_id, string sequence, string mailbox [, int options])","Move specified message to a mailbox"],imap_mailboxmsginfo:["object imap_mailboxmsginfo(resource stream_id)","Returns info about the current mailbox"],imap_mime_header_decode:["array imap_mime_header_decode(string str)","Decode mime header element in accordance with RFC 2047 and return array of objects containing 'charset' encoding and decoded 'text'"],imap_msgno:["int imap_msgno(resource stream_id, int unique_msg_id)","Get the sequence number associated with a UID"],imap_mutf7_to_utf8:["string imap_mutf7_to_utf8(string in)","Decode a modified UTF-7 string to UTF-8"],imap_num_msg:["int imap_num_msg(resource stream_id)","Gives the number of messages in the current mailbox"],imap_num_recent:["int imap_num_recent(resource stream_id)","Gives the number of recent messages in current mailbox"],imap_open:["resource imap_open(string mailbox, string user, string password [, int options [, int n_retries]])","Open an IMAP stream to a mailbox"],imap_ping:["bool imap_ping(resource stream_id)","Check if the IMAP stream is still active"],imap_qprint:["string imap_qprint(string text)","Convert a quoted-printable string to an 8-bit string"],imap_renamemailbox:["bool imap_renamemailbox(resource stream_id, string old_name, string new_name)","Rename a mailbox"],imap_reopen:["bool imap_reopen(resource stream_id, string mailbox [, int options [, int n_retries]])","Reopen an IMAP stream to a new mailbox"],imap_rfc822_parse_adrlist:["array imap_rfc822_parse_adrlist(string address_string, string default_host)","Parses an address string"],imap_rfc822_parse_headers:["object imap_rfc822_parse_headers(string headers [, string default_host])","Parse a set of mail headers contained in a string, and return an object similar to imap_headerinfo()"],imap_rfc822_write_address:["string imap_rfc822_write_address(string mailbox, string host, string personal)","Returns a properly formatted email address given the mailbox, host, and personal info"],imap_savebody:['bool imap_savebody(resource stream_id, string|resource file, int msg_no[, string section = ""[, int options = 0]])',"Save a specific body section to a file"],imap_search:["array imap_search(resource stream_id, string criteria [, int options [, string charset]])","Return a list of messages matching the given criteria"],imap_set_quota:["bool imap_set_quota(resource stream_id, string qroot, int mailbox_size)","Will set the quota for qroot mailbox"],imap_setacl:["bool imap_setacl(resource stream_id, string mailbox, string id, string rights)","Sets the ACL for a given mailbox"],imap_setflag_full:["bool imap_setflag_full(resource stream_id, string sequence, string flag [, int options])","Sets flags on messages"],imap_sort:["array imap_sort(resource stream_id, int criteria, int reverse [, int options [, string search_criteria [, string charset]]])","Sort an array of message headers, optionally including only messages that meet specified criteria."],imap_status:["object imap_status(resource stream_id, string mailbox, int options)","Get status info from a mailbox"],imap_subscribe:["bool imap_subscribe(resource stream_id, string mailbox)","Subscribe to a mailbox"],imap_thread:["array imap_thread(resource stream_id [, int options])","Return threaded by REFERENCES tree"],imap_timeout:["mixed imap_timeout(int timeout_type [, int timeout])","Set or fetch imap timeout"],imap_uid:["int imap_uid(resource stream_id, int msg_no)","Get the unique message id associated with a standard sequential message number"],imap_undelete:["bool imap_undelete(resource stream_id, int msg_no [, int flags])","Remove the delete flag from a message"],imap_unsubscribe:["bool imap_unsubscribe(resource stream_id, string mailbox)","Unsubscribe from a mailbox"],imap_utf7_decode:["string imap_utf7_decode(string buf)","Decode a modified UTF-7 string"],imap_utf7_encode:["string imap_utf7_encode(string buf)","Encode a string in modified UTF-7"],imap_utf8:["string imap_utf8(string mime_encoded_text)","Convert a mime-encoded text to UTF-8"],imap_utf8_to_mutf7:["string imap_utf8_to_mutf7(string in)","Encode a UTF-8 string to modified UTF-7"],implode:["string implode([string glue,] array pieces)","Joins array elements placing glue string between items and return one string"],import_request_variables:["bool import_request_variables(string types [, string prefix])","Import GET/POST/Cookie variables into the global scope"],in_array:["bool in_array(mixed needle, array haystack [, bool strict])","Checks if the given value exists in the array"],include:["bool include(string path)","Includes and evaluates the specified file"],include_once:["bool include_once(string path)","Includes and evaluates the specified file"],inet_ntop:["string inet_ntop(string in_addr)","Converts a packed inet address to a human readable IP address string"],inet_pton:["string inet_pton(string ip_address)","Converts a human readable IP address to a packed binary string"],ini_get:["string ini_get(string varname)","Get a configuration option"],ini_get_all:["array ini_get_all([string extension[, bool details = true]])","Get all configuration options"],ini_restore:["void ini_restore(string varname)","Restore the value of a configuration option specified by varname"],ini_set:["string ini_set(string varname, string newvalue)","Set a configuration option, returns false on error and the old value of the configuration option on success"],interface_exists:["bool interface_exists(string classname [, bool autoload])","Checks if the class exists"],intl_error_name:["string intl_error_name()","* Return a string for a given error code. * The string will be the same as the name of the error code constant."],intl_get_error_code:["int intl_get_error_code()","* Get code of the last occured error."],intl_get_error_message:["string intl_get_error_message()","* Get text description of the last occured error."],intl_is_failure:["bool intl_is_failure()","* Check whether the given error code indicates a failure. * Returns true if it does, and false if the code * indicates success or a warning."],intval:["int intval(mixed var [, int base])","Get the integer value of a variable using the optional base for the conversion"],ip2long:["int ip2long(string ip_address)","Converts a string containing an (IPv4) Internet Protocol dotted address into a proper address"],iptcembed:["array iptcembed(string iptcdata, string jpeg_file_name [, int spool])","Embed binary IPTC data into a JPEG image."],iptcparse:["array iptcparse(string iptcdata)","Parse binary IPTC-data into associative array"],is_a:["bool is_a(object object, string class_name)","Returns true if the object is of this class or has this class as one of its parents"],is_array:["bool is_array(mixed var)","Returns true if variable is an array"],is_bool:["bool is_bool(mixed var)","Returns true if variable is a boolean"],is_callable:["bool is_callable(mixed var [, bool syntax_only [, string callable_name]])","Returns true if var is callable."],is_dir:["bool is_dir(string filename)","Returns true if file is directory"],is_executable:["bool is_executable(string filename)","Returns true if file is executable"],is_file:["bool is_file(string filename)","Returns true if file is a regular file"],is_finite:["bool is_finite(float val)","Returns whether argument is finite"],is_float:["bool is_float(mixed var)","Returns true if variable is float point"],is_infinite:["bool is_infinite(float val)","Returns whether argument is infinite"],is_link:["bool is_link(string filename)","Returns true if file is symbolic link"],is_long:["bool is_long(mixed var)","Returns true if variable is a long (integer)"],is_nan:["bool is_nan(float val)","Returns whether argument is not a number"],is_null:["bool is_null(mixed var)","Returns true if variable is null"],is_numeric:["bool is_numeric(mixed value)","Returns true if value is a number or a numeric string"],is_object:["bool is_object(mixed var)","Returns true if variable is an object"],is_readable:["bool is_readable(string filename)","Returns true if file can be read"],is_resource:["bool is_resource(mixed var)","Returns true if variable is a resource"],is_scalar:["bool is_scalar(mixed value)","Returns true if value is a scalar"],is_string:["bool is_string(mixed var)","Returns true if variable is a string"],is_subclass_of:["bool is_subclass_of(object object, string class_name)","Returns true if the object has this class as one of its parents"],is_uploaded_file:["bool is_uploaded_file(string path)","Check if file was created by rfc1867 upload"],is_writable:["bool is_writable(string filename)","Returns true if file can be written"],isset:["bool isset(mixed var [, mixed var])","Determine whether a variable is set"],iterator_apply:["int iterator_apply(Traversable it, mixed function [, mixed params])","Calls a function for every element in an iterator"],iterator_count:["int iterator_count(Traversable it)","Count the elements in an iterator"],iterator_to_array:["array iterator_to_array(Traversable it [, bool use_keys = true])","Copy the iterator into an array"],jddayofweek:["mixed jddayofweek(int juliandaycount [, int mode])","Returns name or number of day of week from julian day count"],jdmonthname:["string jdmonthname(int juliandaycount, int mode)","Returns name of month for julian day count"],jdtofrench:["string jdtofrench(int juliandaycount)","Converts a julian day count to a french republic calendar date"],jdtogregorian:["string jdtogregorian(int juliandaycount)","Converts a julian day count to a gregorian calendar date"],jdtojewish:["string jdtojewish(int juliandaycount [, bool hebrew [, int fl]])","Converts a julian day count to a jewish calendar date"],jdtojulian:["string jdtojulian(int juliandaycount)","Convert a julian day count to a julian calendar date"],jdtounix:["int jdtounix(int jday)","Convert Julian Day to UNIX timestamp"],jewishtojd:["int jewishtojd(int month, int day, int year)","Converts a jewish calendar date to a julian day count"],join:["string join(array src, string glue)","An alias for implode"],jpeg2wbmp:["bool jpeg2wbmp (string f_org, string f_dest, int d_height, int d_width, int threshold)","Convert JPEG image to WBMP image"],json_decode:["mixed json_decode(string json [, bool assoc [, long depth]])","Decodes the JSON representation into a PHP value"],json_encode:["string json_encode(mixed data [, int options])","Returns the JSON representation of a value"],json_last_error:["int json_last_error()","Returns the error code of the last json_decode()."],juliantojd:["int juliantojd(int month, int day, int year)","Converts a julian calendar date to julian day count"],key:["mixed key(array array_arg)","Return the key of the element currently pointed to by the internal array pointer"],krsort:["bool krsort(array &array_arg [, int sort_flags])","Sort an array by key value in reverse order"],ksort:["bool ksort(array &array_arg [, int sort_flags])","Sort an array by key"],lcfirst:["string lcfirst(string str)","Make a string's first character lowercase"],lcg_value:["float lcg_value()","Returns a value from the combined linear congruential generator"],lchgrp:["bool lchgrp(string filename, mixed group)","Change symlink group"],ldap_8859_to_t61:["string ldap_8859_to_t61(string value)","Translate 8859 characters to t61 characters"],ldap_add:["bool ldap_add(resource link, string dn, array entry)","Add entries to LDAP directory"],ldap_bind:["bool ldap_bind(resource link [, string dn [, string password]])","Bind to LDAP directory"],ldap_compare:["bool ldap_compare(resource link, string dn, string attr, string value)","Determine if an entry has a specific value for one of its attributes"],ldap_connect:["resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]])","Connect to an LDAP server"],ldap_count_entries:["int ldap_count_entries(resource link, resource result)","Count the number of entries in a search result"],ldap_delete:["bool ldap_delete(resource link, string dn)","Delete an entry from a directory"],ldap_dn2ufn:["string ldap_dn2ufn(string dn)","Convert DN to User Friendly Naming format"],ldap_err2str:["string ldap_err2str(int errno)","Convert error number to error string"],ldap_errno:["int ldap_errno(resource link)","Get the current ldap error number"],ldap_error:["string ldap_error(resource link)","Get the current ldap error string"],ldap_explode_dn:["array ldap_explode_dn(string dn, int with_attrib)","Splits DN into its component parts"],ldap_first_attribute:["string ldap_first_attribute(resource link, resource result_entry)","Return first attribute"],ldap_first_entry:["resource ldap_first_entry(resource link, resource result)","Return first result id"],ldap_first_reference:["resource ldap_first_reference(resource link, resource result)","Return first reference"],ldap_free_result:["bool ldap_free_result(resource result)","Free result memory"],ldap_get_attributes:["array ldap_get_attributes(resource link, resource result_entry)","Get attributes from a search result entry"],ldap_get_dn:["string ldap_get_dn(resource link, resource result_entry)","Get the DN of a result entry"],ldap_get_entries:["array ldap_get_entries(resource link, resource result)","Get all result entries"],ldap_get_option:["bool ldap_get_option(resource link, int option, mixed retval)","Get the current value of various session-wide parameters"],ldap_get_values_len:["array ldap_get_values_len(resource link, resource result_entry, string attribute)","Get all values with lengths from a result entry"],ldap_list:["resource ldap_list(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])","Single-level search"],ldap_mod_add:["bool ldap_mod_add(resource link, string dn, array entry)","Add attribute values to current"],ldap_mod_del:["bool ldap_mod_del(resource link, string dn, array entry)","Delete attribute values"],ldap_mod_replace:["bool ldap_mod_replace(resource link, string dn, array entry)","Replace attribute values with new ones"],ldap_next_attribute:["string ldap_next_attribute(resource link, resource result_entry)","Get the next attribute in result"],ldap_next_entry:["resource ldap_next_entry(resource link, resource result_entry)","Get next result entry"],ldap_next_reference:["resource ldap_next_reference(resource link, resource reference_entry)","Get next reference"],ldap_parse_reference:["bool ldap_parse_reference(resource link, resource reference_entry, array referrals)","Extract information from reference entry"],ldap_parse_result:["bool ldap_parse_result(resource link, resource result, int errcode, string matcheddn, string errmsg, array referrals)","Extract information from result"],ldap_read:["resource ldap_read(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])","Read an entry"],ldap_rename:["bool ldap_rename(resource link, string dn, string newrdn, string newparent, bool deleteoldrdn);","Modify the name of an entry"],ldap_sasl_bind:["bool ldap_sasl_bind(resource link [, string binddn [, string password [, string sasl_mech [, string sasl_realm [, string sasl_authc_id [, string sasl_authz_id [, string props]]]]]]])","Bind to LDAP directory using SASL"],ldap_search:["resource ldap_search(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])","Search LDAP tree under base_dn"],ldap_set_option:["bool ldap_set_option(resource link, int option, mixed newval)","Set the value of various session-wide parameters"],ldap_set_rebind_proc:["bool ldap_set_rebind_proc(resource link, string callback)","Set a callback function to do re-binds on referral chasing."],ldap_sort:["bool ldap_sort(resource link, resource result, string sortfilter)","Sort LDAP result entries"],ldap_start_tls:["bool ldap_start_tls(resource link)","Start TLS"],ldap_t61_to_8859:["string ldap_t61_to_8859(string value)","Translate t61 characters to 8859 characters"],ldap_unbind:["bool ldap_unbind(resource link)","Unbind from LDAP directory"],leak:["void leak(int num_bytes=3)","Cause an intentional memory leak, for testing/debugging purposes"],levenshtein:["int levenshtein(string str1, string str2[, int cost_ins, int cost_rep, int cost_del])","Calculate Levenshtein distance between two strings"],libxml_clear_errors:["void libxml_clear_errors()","Clear last error from libxml"],libxml_disable_entity_loader:["bool libxml_disable_entity_loader([boolean disable])","Disable/Enable ability to load external entities"],libxml_get_errors:["object libxml_get_errors()","Retrieve array of errors"],libxml_get_last_error:["object libxml_get_last_error()","Retrieve last error from libxml"],libxml_set_streams_context:["void libxml_set_streams_context(resource streams_context)","Set the streams context for the next libxml document load or write"],libxml_use_internal_errors:["bool libxml_use_internal_errors([boolean use_errors])","Disable libxml errors and allow user to fetch error information as needed"],link:["int link(string target, string link)","Create a hard link"],linkinfo:["int linkinfo(string filename)","Returns the st_dev field of the UNIX C stat structure describing the link"],litespeed_request_headers:["array litespeed_request_headers(void)","Fetch all HTTP request headers"],litespeed_response_headers:["array litespeed_response_headers(void)","Fetch all HTTP response headers"],locale_accept_from_http:["string locale_accept_from_http(string $http_accept)",null],locale_canonicalize:["static string locale_canonicalize(Locale $loc, string $locale)","* @param string $locale The locale string to canonicalize"],locale_filter_matches:["boolean locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])","* Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm"],locale_get_all_variants:["static array locale_get_all_variants($locale)","* gets an array containing the list of variants, or null"],locale_get_default:["static string locale_get_default( )","Get default locale"],locale_get_keywords:["static array locale_get_keywords(string $locale) {","* return an associative array containing keyword-value * pairs for this locale. The keys are keys to the array (doh!)"],locale_get_primary_language:["static string locale_get_primary_language($locale)","* gets the primary language for the $locale"],locale_get_region:["static string locale_get_region($locale)","* gets the region for the $locale"],locale_get_script:["static string locale_get_script($locale)","* gets the script for the $locale"],locale_lookup:["string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])","* Searchs the items in $langtag for the best match to the language * range"],locale_set_default:["static string locale_set_default( string $locale )","Set default locale"],localeconv:["array localeconv(void)","Returns numeric formatting information based on the current locale"],localtime:["array localtime([int timestamp [, bool associative_array]])","Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array"],log:["float log(float number, [float base])","Returns the natural logarithm of the number, or the base log if base is specified"],log10:["float log10(float number)","Returns the base-10 logarithm of the number"],log1p:["float log1p(float number)","Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero"],long2ip:["string long2ip(int proper_address)","Converts an (IPv4) Internet network address into a string in Internet standard dotted format"],lstat:["array lstat(string filename)","Give information about a file or symbolic link"],ltrim:["string ltrim(string str [, string character_mask])","Strips whitespace from the beginning of a string"],mail:["int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])","Send an email message"],max:["mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])","Return the highest value in an array or a series of arguments"],mb_check_encoding:["bool mb_check_encoding([string var[, string encoding]])","Check if the string is valid for the specified encoding"],mb_convert_case:["string mb_convert_case(string sourcestring, int mode [, string encoding])","Returns a case-folded version of sourcestring"],mb_convert_encoding:["string mb_convert_encoding(string str, string to-encoding [, mixed from-encoding])","Returns converted string in desired encoding"],mb_convert_kana:["string mb_convert_kana(string str [, string option] [, string encoding])","Conversion between full-width character and half-width character (Japanese)"],mb_convert_variables:["string mb_convert_variables(string to-encoding, mixed from-encoding, mixed vars [, ...])","Converts the string resource in variables to desired encoding"],mb_decode_mimeheader:["string mb_decode_mimeheader(string string)",'Decodes the MIME "encoded-word" in the string'],mb_decode_numericentity:["string mb_decode_numericentity(string string, array convmap [, string encoding])","Converts HTML numeric entities to character code"],mb_detect_encoding:["string mb_detect_encoding(string str [, mixed encoding_list [, bool strict]])","Encodings of the given string is returned (as a string)"],mb_detect_order:["bool|array mb_detect_order([mixed encoding-list])","Sets the current detect_order or Return the current detect_order as a array"],mb_encode_mimeheader:["string mb_encode_mimeheader(string str [, string charset [, string transfer-encoding [, string linefeed [, int indent]]]])",'Converts the string to MIME "encoded-word" in the format of =?charset?(B|Q)?encoded_string?='],mb_encode_numericentity:["string mb_encode_numericentity(string string, array convmap [, string encoding])","Converts specified characters to HTML numeric entities"],mb_encoding_aliases:["array mb_encoding_aliases(string encoding)","Returns an array of the aliases of a given encoding name"],mb_ereg:["int mb_ereg(string pattern, string string [, array registers])","Regular expression match for multibyte string"],mb_ereg_match:["bool mb_ereg_match(string pattern, string string [,string option])","Regular expression match for multibyte string"],mb_ereg_replace:["string mb_ereg_replace(string pattern, string replacement, string string [, string option])","Replace regular expression for multibyte string"],mb_ereg_search:["bool mb_ereg_search([string pattern[, string option]])","Regular expression search for multibyte string"],mb_ereg_search_getpos:["int mb_ereg_search_getpos(void)","Get search start position"],mb_ereg_search_getregs:["array mb_ereg_search_getregs(void)","Get matched substring of the last time"],mb_ereg_search_init:["bool mb_ereg_search_init(string string [, string pattern[, string option]])","Initialize string and regular expression for search."],mb_ereg_search_pos:["array mb_ereg_search_pos([string pattern[, string option]])","Regular expression search for multibyte string"],mb_ereg_search_regs:["array mb_ereg_search_regs([string pattern[, string option]])","Regular expression search for multibyte string"],mb_ereg_search_setpos:["bool mb_ereg_search_setpos(int position)","Set search start position"],mb_eregi:["int mb_eregi(string pattern, string string [, array registers])","Case-insensitive regular expression match for multibyte string"],mb_eregi_replace:["string mb_eregi_replace(string pattern, string replacement, string string)","Case insensitive replace regular expression for multibyte string"],mb_get_info:["mixed mb_get_info([string type])","Returns the current settings of mbstring"],mb_http_input:["mixed mb_http_input([string type])","Returns the input encoding"],mb_http_output:["string mb_http_output([string encoding])","Sets the current output_encoding or returns the current output_encoding as a string"],mb_internal_encoding:["string mb_internal_encoding([string encoding])","Sets the current internal encoding or Returns the current internal encoding as a string"],mb_language:["string mb_language([string language])","Sets the current language or Returns the current language as a string"],mb_list_encodings:["mixed mb_list_encodings()","Returns an array of all supported entity encodings"],mb_output_handler:["string mb_output_handler(string contents, int status)","Returns string in output buffer converted to the http_output encoding"],mb_parse_str:["bool mb_parse_str(string encoded_string [, array result])","Parses GET/POST/COOKIE data and sets global variables"],mb_preferred_mime_name:["string mb_preferred_mime_name(string encoding)","Return the preferred MIME name (charset) as a string"],mb_regex_encoding:["string mb_regex_encoding([string encoding])","Returns the current encoding for regex as a string."],mb_regex_set_options:["string mb_regex_set_options([string options])","Set or get the default options for mbregex functions"],mb_send_mail:["int mb_send_mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])","* Sends an email message with MIME scheme"],mb_split:["array mb_split(string pattern, string string [, int limit])","split multibyte string into array by regular expression"],mb_strcut:["string mb_strcut(string str, int start [, int length [, string encoding]])","Returns part of a string"],mb_strimwidth:["string mb_strimwidth(string str, int start, int width [, string trimmarker [, string encoding]])","Trim the string in terminal width"],mb_stripos:["int mb_stripos(string haystack, string needle [, int offset [, string encoding]])","Finds position of first occurrence of a string within another, case insensitive"],mb_stristr:["string mb_stristr(string haystack, string needle[, bool part[, string encoding]])","Finds first occurrence of a string within another, case insensitive"],mb_strlen:["int mb_strlen(string str [, string encoding])","Get character numbers of a string"],mb_strpos:["int mb_strpos(string haystack, string needle [, int offset [, string encoding]])","Find position of first occurrence of a string within another"],mb_strrchr:["string mb_strrchr(string haystack, string needle[, bool part[, string encoding]])","Finds the last occurrence of a character in a string within another"],mb_strrichr:["string mb_strrichr(string haystack, string needle[, bool part[, string encoding]])","Finds the last occurrence of a character in a string within another, case insensitive"],mb_strripos:["int mb_strripos(string haystack, string needle [, int offset [, string encoding]])","Finds position of last occurrence of a string within another, case insensitive"],mb_strrpos:["int mb_strrpos(string haystack, string needle [, int offset [, string encoding]])","Find position of last occurrence of a string within another"],mb_strstr:["string mb_strstr(string haystack, string needle[, bool part[, string encoding]])","Finds first occurrence of a string within another"],mb_strtolower:["string mb_strtolower(string sourcestring [, string encoding])","* Returns a lowercased version of sourcestring"],mb_strtoupper:["string mb_strtoupper(string sourcestring [, string encoding])","* Returns a uppercased version of sourcestring"],mb_strwidth:["int mb_strwidth(string str [, string encoding])","Gets terminal width of a string"],mb_substitute_character:["mixed mb_substitute_character([mixed substchar])","Sets the current substitute_character or returns the current substitute_character"],mb_substr:["string mb_substr(string str, int start [, int length [, string encoding]])","Returns part of a string"],mb_substr_count:["int mb_substr_count(string haystack, string needle [, string encoding])","Count the number of substring occurrences"],mcrypt_cbc:["string mcrypt_cbc(int cipher, string key, string data, int mode, string iv)","CBC crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_cfb:["string mcrypt_cfb(int cipher, string key, string data, int mode, string iv)","CFB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_create_iv:["string mcrypt_create_iv(int size, int source)","Create an initialization vector (IV)"],mcrypt_decrypt:["string mcrypt_decrypt(string cipher, string key, string data, string mode, string iv)","OFB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_ecb:["string mcrypt_ecb(int cipher, string key, string data, int mode, string iv)","ECB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_enc_get_algorithms_name:["string mcrypt_enc_get_algorithms_name(resource td)","Returns the name of the algorithm specified by the descriptor td"],mcrypt_enc_get_block_size:["int mcrypt_enc_get_block_size(resource td)","Returns the block size of the cipher specified by the descriptor td"],mcrypt_enc_get_iv_size:["int mcrypt_enc_get_iv_size(resource td)","Returns the size of the IV in bytes of the algorithm specified by the descriptor td"],mcrypt_enc_get_key_size:["int mcrypt_enc_get_key_size(resource td)","Returns the maximum supported key size in bytes of the algorithm specified by the descriptor td"],mcrypt_enc_get_modes_name:["string mcrypt_enc_get_modes_name(resource td)","Returns the name of the mode specified by the descriptor td"],mcrypt_enc_get_supported_key_sizes:["array mcrypt_enc_get_supported_key_sizes(resource td)","This function decrypts the crypttext"],mcrypt_enc_is_block_algorithm:["bool mcrypt_enc_is_block_algorithm(resource td)","Returns TRUE if the alrogithm is a block algorithms"],mcrypt_enc_is_block_algorithm_mode:["bool mcrypt_enc_is_block_algorithm_mode(resource td)","Returns TRUE if the mode is for use with block algorithms"],mcrypt_enc_is_block_mode:["bool mcrypt_enc_is_block_mode(resource td)","Returns TRUE if the mode outputs blocks"],mcrypt_enc_self_test:["int mcrypt_enc_self_test(resource td)","This function runs the self test on the algorithm specified by the descriptor td"],mcrypt_encrypt:["string mcrypt_encrypt(string cipher, string key, string data, string mode, string iv)","OFB crypt/decrypt data using key key with cipher cipher starting with iv"],mcrypt_generic:["string mcrypt_generic(resource td, string data)","This function encrypts the plaintext"],mcrypt_generic_deinit:["bool mcrypt_generic_deinit(resource td)","This function terminates encrypt specified by the descriptor td"],mcrypt_generic_init:["int mcrypt_generic_init(resource td, string key, string iv)","This function initializes all buffers for the specific module"],mcrypt_get_block_size:["int mcrypt_get_block_size(string cipher, string module)","Get the key size of cipher"],mcrypt_get_cipher_name:["string mcrypt_get_cipher_name(string cipher)","Get the key size of cipher"],mcrypt_get_iv_size:["int mcrypt_get_iv_size(string cipher, string module)","Get the IV size of cipher (Usually the same as the blocksize)"],mcrypt_get_key_size:["int mcrypt_get_key_size(string cipher, string module)","Get the key size of cipher"],mcrypt_list_algorithms:["array mcrypt_list_algorithms([string lib_dir])",'List all algorithms in "module_dir"'],mcrypt_list_modes:["array mcrypt_list_modes([string lib_dir])",'List all modes "module_dir"'],mcrypt_module_close:["bool mcrypt_module_close(resource td)","Free the descriptor td"],mcrypt_module_get_algo_block_size:["int mcrypt_module_get_algo_block_size(string algorithm [, string lib_dir])","Returns the block size of the algorithm"],mcrypt_module_get_algo_key_size:["int mcrypt_module_get_algo_key_size(string algorithm [, string lib_dir])","Returns the maximum supported key size of the algorithm"],mcrypt_module_get_supported_key_sizes:["array mcrypt_module_get_supported_key_sizes(string algorithm [, string lib_dir])","This function decrypts the crypttext"],mcrypt_module_is_block_algorithm:["bool mcrypt_module_is_block_algorithm(string algorithm [, string lib_dir])","Returns TRUE if the algorithm is a block algorithm"],mcrypt_module_is_block_algorithm_mode:["bool mcrypt_module_is_block_algorithm_mode(string mode [, string lib_dir])","Returns TRUE if the mode is for use with block algorithms"],mcrypt_module_is_block_mode:["bool mcrypt_module_is_block_mode(string mode [, string lib_dir])","Returns TRUE if the mode outputs blocks of bytes"],mcrypt_module_open:["resource mcrypt_module_open(string cipher, string cipher_directory, string mode, string mode_directory)","Opens the module of the algorithm and the mode to be used"],mcrypt_module_self_test:["bool mcrypt_module_self_test(string algorithm [, string lib_dir])",'Does a self test of the module "module"'],mcrypt_ofb:["string mcrypt_ofb(int cipher, string key, string data, int mode, string iv)","OFB crypt/decrypt data using key key with cipher cipher starting with iv"],md5:["string md5(string str, [ bool raw_output])","Calculate the md5 hash of a string"],md5_file:["string md5_file(string filename [, bool raw_output])","Calculate the md5 hash of given filename"],mdecrypt_generic:["string mdecrypt_generic(resource td, string data)","This function decrypts the plaintext"],memory_get_peak_usage:["int memory_get_peak_usage([real_usage])","Returns the peak allocated by PHP memory"],memory_get_usage:["int memory_get_usage([real_usage])","Returns the allocated by PHP memory"],metaphone:["string metaphone(string text[, int phones])","Break english phrases down into their phonemes"],method_exists:["bool method_exists(object object, string method)","Checks if the class method exists"],mhash:["string mhash(int hash, string data [, string key])","Hash data with hash"],mhash_count:["int mhash_count(void)","Gets the number of available hashes"],mhash_get_block_size:["int mhash_get_block_size(int hash)","Gets the block size of hash"],mhash_get_hash_name:["string mhash_get_hash_name(int hash)","Gets the name of hash"],mhash_keygen_s2k:["string mhash_keygen_s2k(int hash, string input_password, string salt, int bytes)","Generates a key using hash functions"],microtime:["mixed microtime([bool get_as_float])","Returns either a string or a float containing the current time in seconds and microseconds"],mime_content_type:["string mime_content_type(string filename|resource stream)","Return content-type for file"],min:["mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])","Return the lowest value in an array or a series of arguments"],mkdir:["bool mkdir(string pathname [, int mode [, bool recursive [, resource context]]])","Create a directory"],mktime:["int mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])","Get UNIX timestamp for a date"],money_format:["string money_format(string format , float value)","Convert monetary value(s) to string"],move_uploaded_file:["bool move_uploaded_file(string path, string new_path)","Move a file if and only if it was created by an upload"],msg_get_queue:["resource msg_get_queue(int key [, int perms])","Attach to a message queue"],msg_queue_exists:["bool msg_queue_exists(int key)","Check wether a message queue exists"],msg_receive:["mixed msg_receive(resource queue, int desiredmsgtype, int &msgtype, int maxsize, mixed message [, bool unserialize=true [, int flags=0 [, int errorcode]]])","Send a message of type msgtype (must be > 0) to a message queue"],msg_remove_queue:["bool msg_remove_queue(resource queue)","Destroy the queue"],msg_send:["bool msg_send(resource queue, int msgtype, mixed message [, bool serialize=true [, bool blocking=true [, int errorcode]]])","Send a message of type msgtype (must be > 0) to a message queue"],msg_set_queue:["bool msg_set_queue(resource queue, array data)","Set information for a message queue"],msg_stat_queue:["array msg_stat_queue(resource queue)","Returns information about a message queue"],msgfmt_create:["MessageFormatter msgfmt_create( string $locale, string $pattern )","* Create formatter."],msgfmt_format:["mixed msgfmt_format( MessageFormatter $nf, array $args )","* Format a message."],msgfmt_format_message:["mixed msgfmt_format_message( string $locale, string $pattern, array $args )","* Format a message."],msgfmt_get_error_code:["int msgfmt_get_error_code( MessageFormatter $nf )","* Get formatter's last error code."],msgfmt_get_error_message:["string msgfmt_get_error_message( MessageFormatter $coll )","* Get text description for formatter's last error code."],msgfmt_get_locale:["string msgfmt_get_locale(MessageFormatter $mf)","* Get formatter locale."],msgfmt_get_pattern:["string msgfmt_get_pattern( MessageFormatter $mf )","* Get formatter pattern."],msgfmt_parse:["array msgfmt_parse( MessageFormatter $nf, string $source )","* Parse a message."],msgfmt_set_pattern:["bool msgfmt_set_pattern( MessageFormatter $mf, string $pattern )","* Set formatter pattern."],mssql_bind:["bool mssql_bind(resource stmt, string param_name, mixed var, int type [, bool is_output [, bool is_null [, int maxlen]]])","Adds a parameter to a stored procedure or a remote stored procedure"],mssql_close:["bool mssql_close([resource conn_id])","Closes a connection to a MS-SQL server"],mssql_connect:["int mssql_connect([string servername [, string username [, string password [, bool new_link]]]])","Establishes a connection to a MS-SQL server"],mssql_data_seek:["bool mssql_data_seek(resource result_id, int offset)","Moves the internal row pointer of the MS-SQL result associated with the specified result identifier to pointer to the specified row number"],mssql_execute:["mixed mssql_execute(resource stmt [, bool skip_results = false])","Executes a stored procedure on a MS-SQL server database"],mssql_fetch_array:["array mssql_fetch_array(resource result_id [, int result_type])","Returns an associative array of the current row in the result set specified by result_id"],mssql_fetch_assoc:["array mssql_fetch_assoc(resource result_id)","Returns an associative array of the current row in the result set specified by result_id"],mssql_fetch_batch:["int mssql_fetch_batch(resource result_index)","Returns the next batch of records"],mssql_fetch_field:["object mssql_fetch_field(resource result_id [, int offset])","Gets information about certain fields in a query result"],mssql_fetch_object:["object mssql_fetch_object(resource result_id)","Returns a pseudo-object of the current row in the result set specified by result_id"],mssql_fetch_row:["array mssql_fetch_row(resource result_id)","Returns an array of the current row in the result set specified by result_id"],mssql_field_length:["int mssql_field_length(resource result_id [, int offset])","Get the length of a MS-SQL field"],mssql_field_name:["string mssql_field_name(resource result_id [, int offset])","Returns the name of the field given by offset in the result set given by result_id"],mssql_field_seek:["bool mssql_field_seek(resource result_id, int offset)","Seeks to the specified field offset"],mssql_field_type:["string mssql_field_type(resource result_id [, int offset])","Returns the type of a field"],mssql_free_result:["bool mssql_free_result(resource result_index)","Free a MS-SQL result index"],mssql_free_statement:["bool mssql_free_statement(resource result_index)","Free a MS-SQL statement index"],mssql_get_last_message:["string mssql_get_last_message(void)","Gets the last message from the MS-SQL server"],mssql_guid_string:["string mssql_guid_string(string binary [,bool short_format])","Converts a 16 byte binary GUID to a string"],mssql_init:["int mssql_init(string sp_name [, resource conn_id])","Initializes a stored procedure or a remote stored procedure"],mssql_min_error_severity:["void mssql_min_error_severity(int severity)","Sets the lower error severity"],mssql_min_message_severity:["void mssql_min_message_severity(int severity)","Sets the lower message severity"],mssql_next_result:["bool mssql_next_result(resource result_id)","Move the internal result pointer to the next result"],mssql_num_fields:["int mssql_num_fields(resource mssql_result_index)","Returns the number of fields fetched in from the result id specified"],mssql_num_rows:["int mssql_num_rows(resource mssql_result_index)","Returns the number of rows fetched in from the result id specified"],mssql_pconnect:["int mssql_pconnect([string servername [, string username [, string password [, bool new_link]]]])","Establishes a persistent connection to a MS-SQL server"],mssql_query:["resource mssql_query(string query [, resource conn_id [, int batch_size]])","Perform an SQL query on a MS-SQL server database"],mssql_result:["string mssql_result(resource result_id, int row, mixed field)","Returns the contents of one cell from a MS-SQL result set"],mssql_rows_affected:["int mssql_rows_affected(resource conn_id)","Returns the number of records affected by the query"],mssql_select_db:["bool mssql_select_db(string database_name [, resource conn_id])","Select a MS-SQL database"],mt_getrandmax:["int mt_getrandmax(void)","Returns the maximum value a random number from Mersenne Twister can have"],mt_rand:["int mt_rand([int min, int max])","Returns a random number from Mersenne Twister"],mt_srand:["void mt_srand([int seed])","Seeds Mersenne Twister random number generator"],mysql_affected_rows:["int mysql_affected_rows([int link_identifier])","Gets number of affected rows in previous MySQL operation"],mysql_client_encoding:["string mysql_client_encoding([int link_identifier])","Returns the default character set for the current connection"],mysql_close:["bool mysql_close([int link_identifier])","Close a MySQL connection"],mysql_connect:["resource mysql_connect([string hostname[:port][:/path/to/socket] [, string username [, string password [, bool new [, int flags]]]]])","Opens a connection to a MySQL Server"],mysql_create_db:["bool mysql_create_db(string database_name [, int link_identifier])","Create a MySQL database"],mysql_data_seek:["bool mysql_data_seek(resource result, int row_number)","Move internal result pointer"],mysql_db_query:["resource mysql_db_query(string database_name, string query [, int link_identifier])","Sends an SQL query to MySQL"],mysql_drop_db:["bool mysql_drop_db(string database_name [, int link_identifier])","Drops (delete) a MySQL database"],mysql_errno:["int mysql_errno([int link_identifier])","Returns the number of the error message from previous MySQL operation"],mysql_error:["string mysql_error([int link_identifier])","Returns the text of the error message from previous MySQL operation"],mysql_escape_string:["string mysql_escape_string(string to_be_escaped)","Escape string for mysql query"],mysql_fetch_array:["array mysql_fetch_array(resource result [, int result_type])","Fetch a result row as an array (associative, numeric or both)"],mysql_fetch_assoc:["array mysql_fetch_assoc(resource result)","Fetch a result row as an associative array"],mysql_fetch_field:["object mysql_fetch_field(resource result [, int field_offset])","Gets column information from a result and return as an object"],mysql_fetch_lengths:["array mysql_fetch_lengths(resource result)","Gets max data size of each column in a result"],mysql_fetch_object:["object mysql_fetch_object(resource result [, string class_name [, NULL|array ctor_params]])","Fetch a result row as an object"],mysql_fetch_row:["array mysql_fetch_row(resource result)","Gets a result row as an enumerated array"],mysql_field_flags:["string mysql_field_flags(resource result, int field_offset)","Gets the flags associated with the specified field in a result"],mysql_field_len:["int mysql_field_len(resource result, int field_offset)","Returns the length of the specified field"],mysql_field_name:["string mysql_field_name(resource result, int field_index)","Gets the name of the specified field in a result"],mysql_field_seek:["bool mysql_field_seek(resource result, int field_offset)","Sets result pointer to a specific field offset"],mysql_field_table:["string mysql_field_table(resource result, int field_offset)","Gets name of the table the specified field is in"],mysql_field_type:["string mysql_field_type(resource result, int field_offset)","Gets the type of the specified field in a result"],mysql_free_result:["bool mysql_free_result(resource result)","Free result memory"],mysql_get_client_info:["string mysql_get_client_info(void)","Returns a string that represents the client library version"],mysql_get_host_info:["string mysql_get_host_info([int link_identifier])","Returns a string describing the type of connection in use, including the server host name"],mysql_get_proto_info:["int mysql_get_proto_info([int link_identifier])","Returns the protocol version used by current connection"],mysql_get_server_info:["string mysql_get_server_info([int link_identifier])","Returns a string that represents the server version number"],mysql_info:["string mysql_info([int link_identifier])","Returns a string containing information about the most recent query"],mysql_insert_id:["int mysql_insert_id([int link_identifier])","Gets the ID generated from the previous INSERT operation"],mysql_list_dbs:["resource mysql_list_dbs([int link_identifier])","List databases available on a MySQL server"],mysql_list_fields:["resource mysql_list_fields(string database_name, string table_name [, int link_identifier])","List MySQL result fields"],mysql_list_processes:["resource mysql_list_processes([int link_identifier])","Returns a result set describing the current server threads"],mysql_list_tables:["resource mysql_list_tables(string database_name [, int link_identifier])","List tables in a MySQL database"],mysql_num_fields:["int mysql_num_fields(resource result)","Gets number of fields in a result"],mysql_num_rows:["int mysql_num_rows(resource result)","Gets number of rows in a result"],mysql_pconnect:["resource mysql_pconnect([string hostname[:port][:/path/to/socket] [, string username [, string password [, int flags]]]])","Opens a persistent connection to a MySQL Server"],mysql_ping:["bool mysql_ping([int link_identifier])","Ping a server connection. If no connection then reconnect."],mysql_query:["resource mysql_query(string query [, int link_identifier])","Sends an SQL query to MySQL"],mysql_real_escape_string:["string mysql_real_escape_string(string to_be_escaped [, int link_identifier])","Escape special characters in a string for use in a SQL statement, taking into account the current charset of the connection"],mysql_result:["mixed mysql_result(resource result, int row [, mixed field])","Gets result data"],mysql_select_db:["bool mysql_select_db(string database_name [, int link_identifier])","Selects a MySQL database"],mysql_set_charset:["bool mysql_set_charset(string csname [, int link_identifier])","sets client character set"],mysql_stat:["string mysql_stat([int link_identifier])","Returns a string containing status information"],mysql_thread_id:["int mysql_thread_id([int link_identifier])","Returns the thread id of current connection"],mysql_unbuffered_query:["resource mysql_unbuffered_query(string query [, int link_identifier])","Sends an SQL query to MySQL, without fetching and buffering the result rows"],mysqli_affected_rows:["mixed mysqli_affected_rows(object link)","Get number of affected rows in previous MySQL operation"],mysqli_autocommit:["bool mysqli_autocommit(object link, bool mode)","Turn auto commit on or of"],mysqli_cache_stats:["array mysqli_cache_stats(void)","Returns statistics about the zval cache"],mysqli_change_user:["bool mysqli_change_user(object link, string user, string password, string database)","Change logged-in user of the active connection"],mysqli_character_set_name:["string mysqli_character_set_name(object link)","Returns the name of the character set used for this connection"],mysqli_close:["bool mysqli_close(object link)","Close connection"],mysqli_commit:["bool mysqli_commit(object link)","Commit outstanding actions and close transaction"],mysqli_connect:["object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]])","Open a connection to a mysql server"],mysqli_connect_errno:["int mysqli_connect_errno(void)","Returns the numerical value of the error message from last connect command"],mysqli_connect_error:["string mysqli_connect_error(void)","Returns the text of the error message from previous MySQL operation"],mysqli_data_seek:["bool mysqli_data_seek(object result, int offset)","Move internal result pointer"],mysqli_debug:["void mysqli_debug(string debug)",""],mysqli_dump_debug_info:["bool mysqli_dump_debug_info(object link)",""],mysqli_embedded_server_end:["void mysqli_embedded_server_end(void)",""],mysqli_embedded_server_start:["bool mysqli_embedded_server_start(bool start, array arguments, array groups)","initialize and start embedded server"],mysqli_errno:["int mysqli_errno(object link)","Returns the numerical value of the error message from previous MySQL operation"],mysqli_error:["string mysqli_error(object link)","Returns the text of the error message from previous MySQL operation"],mysqli_fetch_all:["mixed mysqli_fetch_all (object result [,int resulttype])","Fetches all result rows as an associative array, a numeric array, or both"],mysqli_fetch_array:["mixed mysqli_fetch_array (object result [,int resulttype])","Fetch a result row as an associative array, a numeric array, or both"],mysqli_fetch_assoc:["mixed mysqli_fetch_assoc (object result)","Fetch a result row as an associative array"],mysqli_fetch_field:["mixed mysqli_fetch_field (object result)","Get column information from a result and return as an object"],mysqli_fetch_field_direct:["mixed mysqli_fetch_field_direct (object result, int offset)","Fetch meta-data for a single field"],mysqli_fetch_fields:["mixed mysqli_fetch_fields (object result)","Return array of objects containing field meta-data"],mysqli_fetch_lengths:["mixed mysqli_fetch_lengths (object result)","Get the length of each output in a result"],mysqli_fetch_object:["mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]])","Fetch a result row as an object"],mysqli_fetch_row:["array mysqli_fetch_row (object result)","Get a result row as an enumerated array"],mysqli_field_count:["int mysqli_field_count(object link)","Fetch the number of fields returned by the last query for the given link"],mysqli_field_seek:["int mysqli_field_seek(object result, int fieldnr)","Set result pointer to a specified field offset"],mysqli_field_tell:["int mysqli_field_tell(object result)","Get current field offset of result pointer"],mysqli_free_result:["void mysqli_free_result(object result)","Free query result memory for the given result handle"],mysqli_get_charset:["object mysqli_get_charset(object link)","returns a character set object"],mysqli_get_client_info:["string mysqli_get_client_info(void)","Get MySQL client info"],mysqli_get_client_stats:["array mysqli_get_client_stats(void)","Returns statistics about the zval cache"],mysqli_get_client_version:["int mysqli_get_client_version(void)","Get MySQL client info"],mysqli_get_connection_stats:["array mysqli_get_connection_stats(void)","Returns statistics about the zval cache"],mysqli_get_host_info:["string mysqli_get_host_info (object link)","Get MySQL host info"],mysqli_get_proto_info:["int mysqli_get_proto_info(object link)","Get MySQL protocol information"],mysqli_get_server_info:["string mysqli_get_server_info(object link)","Get MySQL server info"],mysqli_get_server_version:["int mysqli_get_server_version(object link)","Return the MySQL version for the server referenced by the given link"],mysqli_get_warnings:["object mysqli_get_warnings(object link) */",'PHP_FUNCTION(mysqli_get_warnings) { MY_MYSQL *mysql; zval *mysql_link; MYSQLI_RESOURCE *mysqli_resource; MYSQLI_WARNING *w; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); if (mysql_warning_count(mysql->mysql)) { w = php_get_warnings(mysql->mysql TSRMLS_CC); } else { RETURN_FALSE; } mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); mysqli_resource->ptr = mysqli_resource->info = (void *)w; mysqli_resource->status = MYSQLI_STATUS_VALID; MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry); } /* }}}'],mysqli_info:["string mysqli_info(object link)","Get information about the most recent query"],mysqli_init:["resource mysqli_init(void)","Initialize mysqli and return a resource for use with mysql_real_connect"],mysqli_insert_id:["mixed mysqli_insert_id(object link)","Get the ID generated from the previous INSERT operation"],mysqli_kill:["bool mysqli_kill(object link, int processid)","Kill a mysql process on the server"],mysqli_link_construct:["object mysqli_link_construct()",""],mysqli_more_results:["bool mysqli_more_results(object link)","check if there any more query results from a multi query"],mysqli_multi_query:["bool mysqli_multi_query(object link, string query)","allows to execute multiple queries"],mysqli_next_result:["bool mysqli_next_result(object link)","read next result from multi_query"],mysqli_num_fields:["int mysqli_num_fields(object result)","Get number of fields in result"],mysqli_num_rows:["mixed mysqli_num_rows(object result)","Get number of rows in result"],mysqli_options:["bool mysqli_options(object link, int flags, mixed values)","Set options"],mysqli_ping:["bool mysqli_ping(object link)","Ping a server connection or reconnect if there is no connection"],mysqli_poll:["int mysqli_poll(array read, array write, array error, long sec [, long usec])","Poll connections"],mysqli_prepare:["mixed mysqli_prepare(object link, string query)","Prepare a SQL statement for execution"],mysqli_query:["mixed mysqli_query(object link, string query [,int resultmode]) */",'PHP_FUNCTION(mysqli_query) { MY_MYSQL *mysql; zval *mysql_link; MYSQLI_RESOURCE *mysqli_resource; MYSQL_RES *result; char *query = NULL; unsigned int query_len; unsigned long resultmode = MYSQLI_STORE_RESULT; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) { return; } if (!query_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query"); RETURN_FALSE; } if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode"); RETURN_FALSE; } MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID); MYSQLI_DISABLE_MQ; #ifdef MYSQLI_USE_MYSQLND if (resultmode & MYSQLI_ASYNC) { if (mysqli_async_query(mysql->mysql, query, query_len)) { MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } mysql->async_result_fetch_type = resultmode & ~MYSQLI_ASYNC; RETURN_TRUE; } #endif if (mysql_real_query(mysql->mysql, query, query_len)) { MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } if (!mysql_field_count(mysql->mysql)) { /* no result set - not a SELECT'],mysqli_real_connect:["bool mysqli_real_connect(object link [,string hostname [,string username [,string passwd [,string dbname [,int port [,string socket [,int flags]]]]]]])","Open a connection to a mysql server"],mysqli_real_escape_string:["string mysqli_real_escape_string(object link, string escapestr)","Escapes special characters in a string for use in a SQL statement, taking into account the current charset of the connection"],mysqli_real_query:["bool mysqli_real_query(object link, string query)","Binary-safe version of mysql_query()"],mysqli_reap_async_query:["int mysqli_reap_async_query(object link)","Poll connections"],mysqli_refresh:["bool mysqli_refresh(object link, long options)","Flush tables or caches, or reset replication server information"],mysqli_report:["bool mysqli_report(int flags)","sets report level"],mysqli_rollback:["bool mysqli_rollback(object link)","Undo actions from current transaction"],mysqli_select_db:["bool mysqli_select_db(object link, string dbname)","Select a MySQL database"],mysqli_set_charset:["bool mysqli_set_charset(object link, string csname)","sets client character set"],mysqli_set_local_infile_default:["void mysqli_set_local_infile_default(object link)","unsets user defined handler for load local infile command"],mysqli_set_local_infile_handler:["bool mysqli_set_local_infile_handler(object link, callback read_func)","Set callback functions for LOAD DATA LOCAL INFILE"],mysqli_sqlstate:["string mysqli_sqlstate(object link)","Returns the SQLSTATE error from previous MySQL operation"],mysqli_ssl_set:["bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher])",""],mysqli_stat:["mixed mysqli_stat(object link)","Get current system status"],mysqli_stmt_affected_rows:["mixed mysqli_stmt_affected_rows(object stmt)","Return the number of rows affected in the last query for the given link"],mysqli_stmt_attr_get:["int mysqli_stmt_attr_get(object stmt, long attr)",""],mysqli_stmt_attr_set:["int mysqli_stmt_attr_set(object stmt, long attr, long mode)",""],mysqli_stmt_bind_param:["bool mysqli_stmt_bind_param(object stmt, string types, mixed variable [,mixed,....])","Bind variables to a prepared statement as parameters"],mysqli_stmt_bind_result:["bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...])","Bind variables to a prepared statement for result storage"],mysqli_stmt_close:["bool mysqli_stmt_close(object stmt)","Close statement"],mysqli_stmt_data_seek:["void mysqli_stmt_data_seek(object stmt, int offset)","Move internal result pointer"],mysqli_stmt_errno:["int mysqli_stmt_errno(object stmt)",""],mysqli_stmt_error:["string mysqli_stmt_error(object stmt)",""],mysqli_stmt_execute:["bool mysqli_stmt_execute(object stmt)","Execute a prepared statement"],mysqli_stmt_fetch:["mixed mysqli_stmt_fetch(object stmt)","Fetch results from a prepared statement into the bound variables"],mysqli_stmt_field_count:["int mysqli_stmt_field_count(object stmt) {","Return the number of result columns for the given statement"],mysqli_stmt_free_result:["void mysqli_stmt_free_result(object stmt)","Free stored result memory for the given statement handle"],mysqli_stmt_get_result:["object mysqli_stmt_get_result(object link)","Buffer result set on client"],mysqli_stmt_get_warnings:["object mysqli_stmt_get_warnings(object link) */",'PHP_FUNCTION(mysqli_stmt_get_warnings) { MY_STMT *stmt; zval *stmt_link; MYSQLI_RESOURCE *mysqli_resource; MYSQLI_WARNING *w; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE(stmt, MY_STMT*, &stmt_link, "mysqli_stmt", MYSQLI_STATUS_VALID); if (mysqli_stmt_warning_count(stmt->stmt)) { w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt) TSRMLS_CC); } else { RETURN_FALSE; } mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE)); mysqli_resource->ptr = mysqli_resource->info = (void *)w; mysqli_resource->status = MYSQLI_STATUS_VALID; MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry); } /* }}}'],mysqli_stmt_init:["mixed mysqli_stmt_init(object link)","Initialize statement object"],mysqli_stmt_insert_id:["mixed mysqli_stmt_insert_id(object stmt)","Get the ID generated from the previous INSERT operation"],mysqli_stmt_next_result:["bool mysqli_stmt_next_result(object link)","read next result from multi_query"],mysqli_stmt_num_rows:["mixed mysqli_stmt_num_rows(object stmt)","Return the number of rows in statements result set"],mysqli_stmt_param_count:["int mysqli_stmt_param_count(object stmt)","Return the number of parameter for the given statement"],mysqli_stmt_prepare:["bool mysqli_stmt_prepare(object stmt, string query)","prepare server side statement with query"],mysqli_stmt_reset:["bool mysqli_stmt_reset(object stmt)","reset a prepared statement"],mysqli_stmt_result_metadata:["mixed mysqli_stmt_result_metadata(object stmt)","return result set from statement"],mysqli_stmt_send_long_data:["bool mysqli_stmt_send_long_data(object stmt, int param_nr, string data)",""],mysqli_stmt_sqlstate:["string mysqli_stmt_sqlstate(object stmt)",""],mysqli_stmt_store_result:["bool mysqli_stmt_store_result(stmt)",""],mysqli_store_result:["object mysqli_store_result(object link)","Buffer result set on client"],mysqli_thread_id:["int mysqli_thread_id(object link)","Return the current thread ID"],mysqli_thread_safe:["bool mysqli_thread_safe(void)","Return whether thread safety is given or not"],mysqli_use_result:["mixed mysqli_use_result(object link)","Directly retrieve query results - do not buffer results on client side"],mysqli_warning_count:["int mysqli_warning_count (object link)","Return number of warnings from the last query for the given link"],natcasesort:["void natcasesort(array &array_arg)","Sort an array using case-insensitive natural sort"],natsort:["void natsort(array &array_arg)","Sort an array using natural sort"],next:["mixed next(array array_arg)","Move array argument's internal pointer to the next element and return it"],ngettext:["string ngettext(string MSGID1, string MSGID2, int N)","Plural version of gettext()"],nl2br:["string nl2br(string str [, bool is_xhtml])","Converts newlines to HTML line breaks"],nl_langinfo:["string nl_langinfo(int item)","Query language and locale information"],normalizer_is_normalize:["bool normalizer_is_normalize( string $input [, string $form = FORM_C] )","* Test if a string is in a given normalization form."],normalizer_normalize:["string normalizer_normalize( string $input [, string $form = FORM_C] )","* Normalize a string."],nsapi_request_headers:["array nsapi_request_headers(void)","Get all headers from the request"],nsapi_response_headers:["array nsapi_response_headers(void)","Get all headers from the response"],nsapi_virtual:["bool nsapi_virtual(string uri)","Perform an NSAPI sub-request"],number_format:["string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])","Formats a number with grouped thousands"],numfmt_create:["NumberFormatter numfmt_create( string $locale, int style[, string $pattern ] )","* Create number formatter."],numfmt_format:["mixed numfmt_format( NumberFormatter $nf, mixed $num[, int type] )","* Format a number."],numfmt_format_currency:["mixed numfmt_format_currency( NumberFormatter $nf, double $num, string $currency )","* Format a number as currency."],numfmt_get_attribute:["mixed numfmt_get_attribute( NumberFormatter $nf, int $attr )","* Get formatter attribute value."],numfmt_get_error_code:["int numfmt_get_error_code( NumberFormatter $nf )","* Get formatter's last error code."],numfmt_get_error_message:["string numfmt_get_error_message( NumberFormatter $nf )","* Get text description for formatter's last error code."],numfmt_get_locale:["string numfmt_get_locale( NumberFormatter $nf[, int type] )","* Get formatter locale."],numfmt_get_pattern:["string numfmt_get_pattern( NumberFormatter $nf )","* Get formatter pattern."],numfmt_get_symbol:["string numfmt_get_symbol( NumberFormatter $nf, int $attr )","* Get formatter symbol value."],numfmt_get_text_attribute:["string numfmt_get_text_attribute( NumberFormatter $nf, int $attr )","* Get formatter attribute value."],numfmt_parse:["mixed numfmt_parse( NumberFormatter $nf, string $str[, int $type, int &$position ])","* Parse a number."],numfmt_parse_currency:["double numfmt_parse_currency( NumberFormatter $nf, string $str, string $¤cy[, int $&position] )","* Parse a number as currency."],numfmt_parse_message:["array numfmt_parse_message( string $locale, string $pattern, string $source )","* Parse a message."],numfmt_set_attribute:["bool numfmt_set_attribute( NumberFormatter $nf, int $attr, mixed $value )","* Get formatter attribute value."],numfmt_set_pattern:["bool numfmt_set_pattern( NumberFormatter $nf, string $pattern )","* Set formatter pattern."],numfmt_set_symbol:["bool numfmt_set_symbol( NumberFormatter $nf, int $attr, string $symbol )","* Set formatter symbol value."],numfmt_set_text_attribute:["bool numfmt_set_text_attribute( NumberFormatter $nf, int $attr, string $value )","* Get formatter attribute value."],ob_clean:["bool ob_clean(void)","Clean (delete) the current output buffer"],ob_end_clean:["bool ob_end_clean(void)","Clean the output buffer, and delete current output buffer"],ob_end_flush:["bool ob_end_flush(void)","Flush (send) the output buffer, and delete current output buffer"],ob_flush:["bool ob_flush(void)","Flush (send) contents of the output buffer. The last buffer content is sent to next buffer"],ob_get_clean:["bool ob_get_clean(void)","Get current buffer contents and delete current output buffer"],ob_get_contents:["string ob_get_contents(void)","Return the contents of the output buffer"],ob_get_flush:["bool ob_get_flush(void)","Get current buffer contents, flush (send) the output buffer, and delete current output buffer"],ob_get_length:["int ob_get_length(void)","Return the length of the output buffer"],ob_get_level:["int ob_get_level(void)","Return the nesting level of the output buffer"],ob_get_status:["false|array ob_get_status([bool full_status])","Return the status of the active or all output buffers"],ob_gzhandler:["string ob_gzhandler(string str, int mode)","Encode str based on accept-encoding setting - designed to be called from ob_start()"],ob_iconv_handler:["string ob_iconv_handler(string contents, int status)","Returns str in output buffer converted to the iconv.output_encoding character set"],ob_implicit_flush:["void ob_implicit_flush([int flag])","Turn implicit flush on/off and is equivalent to calling flush() after every output call"],ob_list_handlers:["false|array ob_list_handlers()","* List all output_buffers in an array"],ob_start:["bool ob_start([ string|array user_function [, int chunk_size [, bool erase]]])","Turn on Output Buffering (specifying an optional output handler)."],oci_bind_array_by_name:["bool oci_bind_array_by_name(resource stmt, string name, array &var, int max_table_length [, int max_item_length [, int type ]])","Bind a PHP array to an Oracle PL/SQL type by name"],oci_bind_by_name:["bool oci_bind_by_name(resource stmt, string name, mixed &var, [, int maxlength [, int type]])","Bind a PHP variable to an Oracle placeholder by name"],oci_cancel:["bool oci_cancel(resource stmt)","Cancel reading from a cursor"],oci_close:["bool oci_close(resource connection)","Disconnect from database"],oci_collection_append:["bool oci_collection_append(string value)","Append an object to the collection"],oci_collection_assign:["bool oci_collection_assign(object from)","Assign a collection from another existing collection"],oci_collection_element_assign:["bool oci_collection_element_assign(int index, string val)","Assign element val to collection at index ndx"],oci_collection_element_get:["string oci_collection_element_get(int ndx)","Retrieve the value at collection index ndx"],oci_collection_max:["int oci_collection_max()","Return the max value of a collection. For a varray this is the maximum length of the array"],oci_collection_size:["int oci_collection_size()","Return the size of a collection"],oci_collection_trim:["bool oci_collection_trim(int num)","Trim num elements from the end of a collection"],oci_commit:["bool oci_commit(resource connection)","Commit the current context"],oci_connect:["resource oci_connect(string user, string pass [, string db [, string charset [, int session_mode ]])","Connect to an Oracle database and log on. Returns a new session."],oci_define_by_name:["bool oci_define_by_name(resource stmt, string name, mixed &var [, int type])","Define a PHP variable to an Oracle column by name"],oci_error:["array oci_error([resource stmt|connection|global])","Return the last error of stmt|connection|global. If no error happened returns false."],oci_execute:["bool oci_execute(resource stmt [, int mode])","Execute a parsed statement"],oci_fetch:["bool oci_fetch(resource stmt)","Prepare a new row of data for reading"],oci_fetch_all:["int oci_fetch_all(resource stmt, array &output[, int skip[, int maxrows[, int flags]]])","Fetch all rows of result data into an array"],oci_fetch_array:["array oci_fetch_array( resource stmt [, int mode ])","Fetch a result row as an array"],oci_fetch_assoc:["array oci_fetch_assoc( resource stmt )","Fetch a result row as an associative array"],oci_fetch_object:["object oci_fetch_object( resource stmt )","Fetch a result row as an object"],oci_fetch_row:["array oci_fetch_row( resource stmt )","Fetch a result row as an enumerated array"],oci_field_is_null:["bool oci_field_is_null(resource stmt, int col)","Tell whether a column is NULL"],oci_field_name:["string oci_field_name(resource stmt, int col)","Tell the name of a column"],oci_field_precision:["int oci_field_precision(resource stmt, int col)","Tell the precision of a column"],oci_field_scale:["int oci_field_scale(resource stmt, int col)","Tell the scale of a column"],oci_field_size:["int oci_field_size(resource stmt, int col)","Tell the maximum data size of a column"],oci_field_type:["mixed oci_field_type(resource stmt, int col)","Tell the data type of a column"],oci_field_type_raw:["int oci_field_type_raw(resource stmt, int col)","Tell the raw oracle data type of a column"],oci_free_collection:["bool oci_free_collection()","Deletes collection object"],oci_free_descriptor:["bool oci_free_descriptor()","Deletes large object description"],oci_free_statement:["bool oci_free_statement(resource stmt)","Free all resources associated with a statement"],oci_internal_debug:["void oci_internal_debug(int onoff)","Toggle internal debugging output for the OCI extension"],oci_lob_append:["bool oci_lob_append( object lob )","Appends data from a LOB to another LOB"],oci_lob_close:["bool oci_lob_close()","Closes lob descriptor"],oci_lob_copy:["bool oci_lob_copy( object lob_to, object lob_from [, int length ] )","Copies data from a LOB to another LOB"],oci_lob_eof:["bool oci_lob_eof()","Checks if EOF is reached"],oci_lob_erase:["int oci_lob_erase( [ int offset [, int length ] ] )","Erases a specified portion of the internal LOB, starting at a specified offset"],oci_lob_export:["bool oci_lob_export([string filename [, int start [, int length]]])","Writes a large object into a file"],oci_lob_flush:["bool oci_lob_flush( [ int flag ] )","Flushes the LOB buffer"],oci_lob_import:["bool oci_lob_import( string filename )","Loads file into a LOB"],oci_lob_is_equal:["bool oci_lob_is_equal( object lob1, object lob2 )","Tests to see if two LOB/FILE locators are equal"],oci_lob_load:["string oci_lob_load()","Loads a large object"],oci_lob_read:["string oci_lob_read( int length )","Reads particular part of a large object"],oci_lob_rewind:["bool oci_lob_rewind()","Rewind pointer of a LOB"],oci_lob_save:["bool oci_lob_save( string data [, int offset ])","Saves a large object"],oci_lob_seek:["bool oci_lob_seek( int offset [, int whence ])","Moves the pointer of a LOB"],oci_lob_size:["int oci_lob_size()","Returns size of a large object"],oci_lob_tell:["int oci_lob_tell()","Tells LOB pointer position"],oci_lob_truncate:["bool oci_lob_truncate( [ int length ])","Truncates a LOB"],oci_lob_write:["int oci_lob_write( string string [, int length ])","Writes data to current position of a LOB"],oci_lob_write_temporary:["bool oci_lob_write_temporary(string var [, int lob_type])","Writes temporary blob"],oci_new_collection:["object oci_new_collection(resource connection, string tdo [, string schema])","Initialize a new collection"],oci_new_connect:["resource oci_new_connect(string user, string pass [, string db])","Connect to an Oracle database and log on. Returns a new session."],oci_new_cursor:["resource oci_new_cursor(resource connection)","Return a new cursor (Statement-Handle) - use this to bind ref-cursors!"],oci_new_descriptor:["object oci_new_descriptor(resource connection [, int type])","Initialize a new empty descriptor LOB/FILE (LOB is default)"],oci_num_fields:["int oci_num_fields(resource stmt)","Return the number of result columns in a statement"],oci_num_rows:["int oci_num_rows(resource stmt)","Return the row count of an OCI statement"],oci_parse:["resource oci_parse(resource connection, string query)","Parse a query and return a statement"],oci_password_change:["bool oci_password_change(resource connection, string username, string old_password, string new_password)","Changes the password of an account"],oci_pconnect:["resource oci_pconnect(string user, string pass [, string db [, string charset ]])","Connect to an Oracle database using a persistent connection and log on. Returns a new session."],oci_result:["string oci_result(resource stmt, mixed column)","Return a single column of result data"],oci_rollback:["bool oci_rollback(resource connection)","Rollback the current context"],oci_server_version:["string oci_server_version(resource connection)","Return a string containing server version information"],oci_set_action:["bool oci_set_action(resource connection, string value)","Sets the action attribute on the connection"],oci_set_client_identifier:["bool oci_set_client_identifier(resource connection, string value)","Sets the client identifier attribute on the connection"],oci_set_client_info:["bool oci_set_client_info(resource connection, string value)","Sets the client info attribute on the connection"],oci_set_edition:["bool oci_set_edition(string value)","Sets the edition attribute for all subsequent connections created"],oci_set_module_name:["bool oci_set_module_name(resource connection, string value)","Sets the module attribute on the connection"],oci_set_prefetch:["bool oci_set_prefetch(resource stmt, int prefetch_rows)","Sets the number of rows to be prefetched on execute to prefetch_rows for stmt"],oci_statement_type:["string oci_statement_type(resource stmt)","Return the query type of an OCI statement"],ocifetchinto:["int ocifetchinto(resource stmt, array &output [, int mode])","Fetch a row of result data into an array"],ocigetbufferinglob:["bool ocigetbufferinglob()","Returns current state of buffering for a LOB"],ocisetbufferinglob:["bool ocisetbufferinglob( boolean flag )","Enables/disables buffering for a LOB"],octdec:["int octdec(string octal_number)","Returns the decimal equivalent of an octal string"],odbc_autocommit:["mixed odbc_autocommit(resource connection_id [, int OnOff])","Toggle autocommit mode or get status"],odbc_binmode:["bool odbc_binmode(int result_id, int mode)","Handle binary column data"],odbc_close:["void odbc_close(resource connection_id)","Close an ODBC connection"],odbc_close_all:["void odbc_close_all(void)","Close all ODBC connections"],odbc_columnprivileges:["resource odbc_columnprivileges(resource connection_id, string catalog, string schema, string table, string column)","Returns a result identifier that can be used to fetch a list of columns and associated privileges for the specified table"],odbc_columns:["resource odbc_columns(resource connection_id [, string qualifier [, string owner [, string table_name [, string column_name]]]])","Returns a result identifier that can be used to fetch a list of column names in specified tables"],odbc_commit:["bool odbc_commit(resource connection_id)","Commit an ODBC transaction"],odbc_connect:["resource odbc_connect(string DSN, string user, string password [, int cursor_option])","Connect to a datasource"],odbc_cursor:["string odbc_cursor(resource result_id)","Get cursor name"],odbc_data_source:["array odbc_data_source(resource connection_id, int fetch_type)","Return information about the currently connected data source"],odbc_error:["string odbc_error([resource connection_id])","Get the last error code"],odbc_errormsg:["string odbc_errormsg([resource connection_id])","Get the last error message"],odbc_exec:["resource odbc_exec(resource connection_id, string query [, int flags])","Prepare and execute an SQL statement"],odbc_execute:["bool odbc_execute(resource result_id [, array parameters_array])","Execute a prepared statement"],odbc_fetch_array:["array odbc_fetch_array(int result [, int rownumber])","Fetch a result row as an associative array"],odbc_fetch_into:["int odbc_fetch_into(resource result_id, array &result_array, [, int rownumber])","Fetch one result row into an array"],odbc_fetch_object:["object odbc_fetch_object(int result [, int rownumber])","Fetch a result row as an object"],odbc_fetch_row:["bool odbc_fetch_row(resource result_id [, int row_number])","Fetch a row"],odbc_field_len:["int odbc_field_len(resource result_id, int field_number)","Get the length (precision) of a column"],odbc_field_name:["string odbc_field_name(resource result_id, int field_number)","Get a column name"],odbc_field_num:["int odbc_field_num(resource result_id, string field_name)","Return column number"],odbc_field_scale:["int odbc_field_scale(resource result_id, int field_number)","Get the scale of a column"],odbc_field_type:["string odbc_field_type(resource result_id, int field_number)","Get the datatype of a column"],odbc_foreignkeys:["resource odbc_foreignkeys(resource connection_id, string pk_qualifier, string pk_owner, string pk_table, string fk_qualifier, string fk_owner, string fk_table)","Returns a result identifier to either a list of foreign keys in the specified table or a list of foreign keys in other tables that refer to the primary key in the specified table"],odbc_free_result:["bool odbc_free_result(resource result_id)","Free resources associated with a result"],odbc_gettypeinfo:["resource odbc_gettypeinfo(resource connection_id [, int data_type])","Returns a result identifier containing information about data types supported by the data source"],odbc_longreadlen:["bool odbc_longreadlen(int result_id, int length)","Handle LONG columns"],odbc_next_result:["bool odbc_next_result(resource result_id)","Checks if multiple results are avaiable"],odbc_num_fields:["int odbc_num_fields(resource result_id)","Get number of columns in a result"],odbc_num_rows:["int odbc_num_rows(resource result_id)","Get number of rows in a result"],odbc_pconnect:["resource odbc_pconnect(string DSN, string user, string password [, int cursor_option])","Establish a persistent connection to a datasource"],odbc_prepare:["resource odbc_prepare(resource connection_id, string query)","Prepares a statement for execution"],odbc_primarykeys:["resource odbc_primarykeys(resource connection_id, string qualifier, string owner, string table)","Returns a result identifier listing the column names that comprise the primary key for a table"],odbc_procedurecolumns:["resource odbc_procedurecolumns(resource connection_id [, string qualifier, string owner, string proc, string column])","Returns a result identifier containing the list of input and output parameters, as well as the columns that make up the result set for the specified procedures"],odbc_procedures:["resource odbc_procedures(resource connection_id [, string qualifier, string owner, string name])","Returns a result identifier containg the list of procedure names in a datasource"],odbc_result:["mixed odbc_result(resource result_id, mixed field)","Get result data"],odbc_result_all:["int odbc_result_all(resource result_id [, string format])","Print result as HTML table"],odbc_rollback:["bool odbc_rollback(resource connection_id)","Rollback a transaction"],odbc_setoption:["bool odbc_setoption(resource conn_id|result_id, int which, int option, int value)","Sets connection or statement options"],odbc_specialcolumns:["resource odbc_specialcolumns(resource connection_id, int type, string qualifier, string owner, string table, int scope, int nullable)","Returns a result identifier containing either the optimal set of columns that uniquely identifies a row in the table or columns that are automatically updated when any value in the row is updated by a transaction"],odbc_statistics:["resource odbc_statistics(resource connection_id, string qualifier, string owner, string name, int unique, int accuracy)","Returns a result identifier that contains statistics about a single table and the indexes associated with the table"],odbc_tableprivileges:["resource odbc_tableprivileges(resource connection_id, string qualifier, string owner, string name)","Returns a result identifier containing a list of tables and the privileges associated with each table"],odbc_tables:["resource odbc_tables(resource connection_id [, string qualifier [, string owner [, string name [, string table_types]]]])","Call the SQLTables function"],opendir:["mixed opendir(string path[, resource context])","Open a directory and return a dir_handle"],openlog:["bool openlog(string ident, int option, int facility)","Open connection to system logger"],openssl_csr_export:["bool openssl_csr_export(resource csr, string &out [, bool notext=true])","Exports a CSR to file or a var"],openssl_csr_export_to_file:["bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])","Exports a CSR to file"],openssl_csr_get_public_key:["mixed openssl_csr_get_public_key(mixed csr)","Returns the subject of a CERT or FALSE on error"],openssl_csr_get_subject:["mixed openssl_csr_get_subject(mixed csr)","Returns the subject of a CERT or FALSE on error"],openssl_csr_new:["bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])","Generates a privkey and CSR"],openssl_csr_sign:["resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]])","Signs a cert with another CERT"],openssl_decrypt:["string openssl_decrypt(string data, string method, string password [, bool raw_input=false])","Takes raw or base64 encoded string and dectupt it using given method and key"],openssl_dh_compute_key:["string openssl_dh_compute_key(string pub_key, resource dh_key)","Computes shared sicret for public value of remote DH key and local DH key"],openssl_digest:["string openssl_digest(string data, string method [, bool raw_output=false])","Computes digest hash value for given data using given method, returns raw or binhex encoded string"],openssl_encrypt:["string openssl_encrypt(string data, string method, string password [, bool raw_output=false])","Encrypts given data with given method and key, returns raw or base64 encoded string"],openssl_error_string:["mixed openssl_error_string(void)","Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages"],openssl_get_cipher_methods:["array openssl_get_cipher_methods([bool aliases = false])","Return array of available cipher methods"],openssl_get_md_methods:["array openssl_get_md_methods([bool aliases = false])","Return array of available digest methods"],openssl_open:["bool openssl_open(string data, &string opendata, string ekey, mixed privkey)","Opens data"],openssl_pkcs12_export:["bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args])","Creates and exports a PKCS12 to a var"],openssl_pkcs12_export_to_file:["bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args])","Creates and exports a PKCS to file"],openssl_pkcs12_read:["bool openssl_pkcs12_read(string PKCS12, array &certs, string pass)","Parses a PKCS12 to an array"],openssl_pkcs7_decrypt:["bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])","Decrypts the S/MIME message in the file name infilename and output the results to the file name outfilename. recipcert is a CERT for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key"],openssl_pkcs7_encrypt:["bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])","Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile"],openssl_pkcs7_sign:["bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])","Signs the MIME message in the file named infile with signcert/signkey and output the result to file name outfile. headers lists plain text headers to exclude from the signed portion of the message, and should include to, from and subject as a minimum"],openssl_pkcs7_verify:["bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])","Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers"],openssl_pkey_export:["bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])","Gets an exportable representation of a key into a string or file"],openssl_pkey_export_to_file:["bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)","Gets an exportable representation of a key into a file"],openssl_pkey_free:["void openssl_pkey_free(int key)","Frees a key"],openssl_pkey_get_details:["resource openssl_pkey_get_details(resource key)","returns an array with the key details (bits, pkey, type)"],openssl_pkey_get_private:["int openssl_pkey_get_private(string key [, string passphrase])","Gets private keys"],openssl_pkey_get_public:["int openssl_pkey_get_public(mixed cert)","Gets public key from X.509 certificate"],openssl_pkey_new:["resource openssl_pkey_new([array configargs])","Generates a new private key"],openssl_private_decrypt:["bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])","Decrypts data with private key"],openssl_private_encrypt:["bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])","Encrypts data with private key"],openssl_public_decrypt:["bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])","Decrypts data with public key"],openssl_public_encrypt:["bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])","Encrypts data with public key"],openssl_random_pseudo_bytes:["string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])","Returns a string of the length specified filled with random pseudo bytes"],openssl_seal:["int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys)","Seals data"],openssl_sign:["bool openssl_sign(string data, &string signature, mixed key[, mixed method])","Signs data"],openssl_verify:["int openssl_verify(string data, string signature, mixed key[, mixed method])","Verifys data"],openssl_x509_check_private_key:["bool openssl_x509_check_private_key(mixed cert, mixed key)","Checks if a private key corresponds to a CERT"],openssl_x509_checkpurpose:["int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile])","Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs"],openssl_x509_export:["bool openssl_x509_export(mixed x509, string &out [, bool notext = true])","Exports a CERT to file or a var"],openssl_x509_export_to_file:["bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true])","Exports a CERT to file or a var"],openssl_x509_free:["void openssl_x509_free(resource x509)","Frees X.509 certificates"],openssl_x509_parse:["array openssl_x509_parse(mixed x509 [, bool shortnames=true])","Returns an array of the fields/values of the CERT"],openssl_x509_read:["resource openssl_x509_read(mixed cert)","Reads X.509 certificates"],ord:["int ord(string character)","Returns ASCII value of character"],output_add_rewrite_var:["bool output_add_rewrite_var(string name, string value)","Add URL rewriter values"],output_reset_rewrite_vars:["bool output_reset_rewrite_vars(void)","Reset(clear) URL rewriter values"],pack:["string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])","Takes one or more arguments and packs them into a binary string according to the format argument"],parse_ini_file:["array parse_ini_file(string filename [, bool process_sections [, int scanner_mode]])","Parse configuration file"],parse_ini_string:["array parse_ini_string(string ini_string [, bool process_sections [, int scanner_mode]])","Parse configuration string"],parse_locale:["static array parse_locale($locale)","* parses a locale-id into an array the different parts of it"],parse_str:["void parse_str(string encoded_string [, array result])","Parses GET/POST/COOKIE data and sets global variables"],parse_url:["mixed parse_url(string url, [int url_component])","Parse a URL and return its components"],passthru:["void passthru(string command [, int &return_value])","Execute an external program and display raw output"],pathinfo:["array pathinfo(string path[, int options])","Returns information about a certain string"],pclose:["int pclose(resource fp)","Close a file pointer opened by popen()"],pcnlt_sigwaitinfo:["int pcnlt_sigwaitinfo(array set[, array &siginfo])","Synchronously wait for queued signals"],pcntl_alarm:["int pcntl_alarm(int seconds)","Set an alarm clock for delivery of a signal"],pcntl_exec:["bool pcntl_exec(string path [, array args [, array envs]])","Executes specified program in current process space as defined by exec(2)"],pcntl_fork:["int pcntl_fork(void)","Forks the currently running process following the same behavior as the UNIX fork() system call"],pcntl_getpriority:["int pcntl_getpriority([int pid [, int process_identifier]])","Get the priority of any process"],pcntl_setpriority:["bool pcntl_setpriority(int priority [, int pid [, int process_identifier]])","Change the priority of any process"],pcntl_signal:["bool pcntl_signal(int signo, callback handle [, bool restart_syscalls])","Assigns a system signal handler to a PHP function"],pcntl_signal_dispatch:["bool pcntl_signal_dispatch()","Dispatch signals to signal handlers"],pcntl_sigprocmask:["bool pcntl_sigprocmask(int how, array set[, array &oldset])","Examine and change blocked signals"],pcntl_sigtimedwait:["int pcntl_sigtimedwait(array set[, array &siginfo[, int seconds[, int nanoseconds]]])","Wait for queued signals"],pcntl_wait:["int pcntl_wait(int &status)","Waits on or returns the status of a forked child as defined by the waitpid() system call"],pcntl_waitpid:["int pcntl_waitpid(int pid, int &status, int options)","Waits on or returns the status of a forked child as defined by the waitpid() system call"],pcntl_wexitstatus:["int pcntl_wexitstatus(int status)","Returns the status code of a child's exit"],pcntl_wifexited:["bool pcntl_wifexited(int status)","Returns true if the child status code represents a successful exit"],pcntl_wifsignaled:["bool pcntl_wifsignaled(int status)","Returns true if the child status code represents a process that was terminated due to a signal"],pcntl_wifstopped:["bool pcntl_wifstopped(int status)","Returns true if the child status code represents a stopped process (WUNTRACED must have been used with waitpid)"],pcntl_wstopsig:["int pcntl_wstopsig(int status)","Returns the number of the signal that caused the process to stop who's status code is passed"],pcntl_wtermsig:["int pcntl_wtermsig(int status)","Returns the number of the signal that terminated the process who's status code is passed"],pdo_drivers:["array pdo_drivers()","Return array of available PDO drivers"],pfsockopen:["resource pfsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]])","Open persistent Internet or Unix domain socket connection"],pg_affected_rows:["int pg_affected_rows(resource result)","Returns the number of affected tuples"],pg_cancel_query:["bool pg_cancel_query(resource connection)","Cancel request"],pg_client_encoding:["string pg_client_encoding([resource connection])","Get the current client encoding"],pg_close:["bool pg_close([resource connection])","Close a PostgreSQL connection"],pg_connect:["resource pg_connect(string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database)","Open a PostgreSQL connection"],pg_connection_busy:["bool pg_connection_busy(resource connection)","Get connection is busy or not"],pg_connection_reset:["bool pg_connection_reset(resource connection)","Reset connection (reconnect)"],pg_connection_status:["int pg_connection_status(resource connnection)","Get connection status"],pg_convert:["array pg_convert(resource db, string table, array values[, int options])","Check and convert values for PostgreSQL SQL statement"],pg_copy_from:["bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])","Copy table from array"],pg_copy_to:["array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])","Copy table to array"],pg_dbname:["string pg_dbname([resource connection])","Get the database name"],pg_delete:["mixed pg_delete(resource db, string table, array ids[, int options])","Delete records has ids (id=>value)"],pg_end_copy:["bool pg_end_copy([resource connection])","Sync with backend. Completes the Copy command"],pg_escape_bytea:["string pg_escape_bytea([resource connection,] string data)","Escape binary for bytea type"],pg_escape_string:["string pg_escape_string([resource connection,] string data)","Escape string for text/char type"],pg_execute:["resource pg_execute([resource connection,] string stmtname, array params)","Execute a prepared query"],pg_fetch_all:["array pg_fetch_all(resource result)","Fetch all rows into array"],pg_fetch_all_columns:["array pg_fetch_all_columns(resource result [, int column_number])","Fetch all rows into array"],pg_fetch_array:["array pg_fetch_array(resource result [, int row [, int result_type]])","Fetch a row as an array"],pg_fetch_assoc:["array pg_fetch_assoc(resource result [, int row])","Fetch a row as an assoc array"],pg_fetch_object:["object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])","Fetch a row as an object"],pg_fetch_result:["mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)","Returns values from a result identifier"],pg_fetch_row:["array pg_fetch_row(resource result [, int row [, int result_type]])","Get a row as an enumerated array"],pg_field_is_null:["int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)","Test if a field is NULL"],pg_field_name:["string pg_field_name(resource result, int field_number)","Returns the name of the field"],pg_field_num:["int pg_field_num(resource result, string field_name)","Returns the field number of the named field"],pg_field_prtlen:["int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)","Returns the printed length"],pg_field_size:["int pg_field_size(resource result, int field_number)","Returns the internal size of the field"],pg_field_table:["mixed pg_field_table(resource result, int field_number[, bool oid_only])","Returns the name of the table field belongs to, or table's oid if oid_only is true"],pg_field_type:["string pg_field_type(resource result, int field_number)","Returns the type name for the given field"],pg_field_type_oid:["string pg_field_type_oid(resource result, int field_number)","Returns the type oid for the given field"],pg_free_result:["bool pg_free_result(resource result)","Free result memory"],pg_get_notify:["array pg_get_notify([resource connection[, result_type]])","Get asynchronous notification"],pg_get_pid:["int pg_get_pid([resource connection)","Get backend(server) pid"],pg_get_result:["resource pg_get_result(resource connection)","Get asynchronous query result"],pg_host:["string pg_host([resource connection])","Returns the host name associated with the connection"],pg_insert:["mixed pg_insert(resource db, string table, array values[, int options])","Insert values (filed=>value) to table"],pg_last_error:["string pg_last_error([resource connection])","Get the error message string"],pg_last_notice:["string pg_last_notice(resource connection)","Returns the last notice set by the backend"],pg_last_oid:["string pg_last_oid(resource result)","Returns the last object identifier"],pg_lo_close:["bool pg_lo_close(resource large_object)","Close a large object"],pg_lo_create:["mixed pg_lo_create([resource connection],[mixed large_object_oid])","Create a large object"],pg_lo_export:["bool pg_lo_export([resource connection, ] int objoid, string filename)","Export large object direct to filesystem"],pg_lo_import:["int pg_lo_import([resource connection, ] string filename [, mixed oid])","Import large object direct from filesystem"],pg_lo_open:["resource pg_lo_open([resource connection,] int large_object_oid, string mode)","Open a large object and return fd"],pg_lo_read:["string pg_lo_read(resource large_object [, int len])","Read a large object"],pg_lo_read_all:["int pg_lo_read_all(resource large_object)","Read a large object and send straight to browser"],pg_lo_seek:["bool pg_lo_seek(resource large_object, int offset [, int whence])","Seeks position of large object"],pg_lo_tell:["int pg_lo_tell(resource large_object)","Returns current position of large object"],pg_lo_unlink:["bool pg_lo_unlink([resource connection,] string large_object_oid)","Delete a large object"],pg_lo_write:["int pg_lo_write(resource large_object, string buf [, int len])","Write a large object"],pg_meta_data:["array pg_meta_data(resource db, string table)","Get meta_data"],pg_num_fields:["int pg_num_fields(resource result)","Return the number of fields in the result"],pg_num_rows:["int pg_num_rows(resource result)","Return the number of rows in the result"],pg_options:["string pg_options([resource connection])","Get the options associated with the connection"],pg_parameter_status:["string|false pg_parameter_status([resource connection,] string param_name)","Returns the value of a server parameter"],pg_pconnect:["resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database)","Open a persistent PostgreSQL connection"],pg_ping:["bool pg_ping([resource connection])","Ping database. If connection is bad, try to reconnect."],pg_port:["int pg_port([resource connection])","Return the port number associated with the connection"],pg_prepare:["resource pg_prepare([resource connection,] string stmtname, string query)","Prepare a query for future execution"],pg_put_line:["bool pg_put_line([resource connection,] string query)","Send null-terminated string to backend server"],pg_query:["resource pg_query([resource connection,] string query)","Execute a query"],pg_query_params:["resource pg_query_params([resource connection,] string query, array params)","Execute a query"],pg_result_error:["string pg_result_error(resource result)","Get error message associated with result"],pg_result_error_field:["string pg_result_error_field(resource result, int fieldcode)","Get error message field associated with result"],pg_result_seek:["bool pg_result_seek(resource result, int offset)","Set internal row offset"],pg_result_status:["mixed pg_result_status(resource result[, long result_type])","Get status of query result"],pg_select:["mixed pg_select(resource db, string table, array ids[, int options])","Select records that has ids (id=>value)"],pg_send_execute:["bool pg_send_execute(resource connection, string stmtname, array params)","Executes prevriously prepared stmtname asynchronously"],pg_send_prepare:["bool pg_send_prepare(resource connection, string stmtname, string query)","Asynchronously prepare a query for future execution"],pg_send_query:["bool pg_send_query(resource connection, string query)","Send asynchronous query"],pg_send_query_params:["bool pg_send_query_params(resource connection, string query, array params)","Send asynchronous parameterized query"],pg_set_client_encoding:["int pg_set_client_encoding([resource connection,] string encoding)","Set client encoding"],pg_set_error_verbosity:["int pg_set_error_verbosity([resource connection,] int verbosity)","Set error verbosity"],pg_trace:["bool pg_trace(string filename [, string mode [, resource connection]])","Enable tracing a PostgreSQL connection"],pg_transaction_status:["int pg_transaction_status(resource connnection)","Get transaction status"],pg_tty:["string pg_tty([resource connection])","Return the tty name associated with the connection"],pg_unescape_bytea:["string pg_unescape_bytea(string data)","Unescape binary for bytea type"],pg_untrace:["bool pg_untrace([resource connection])","Disable tracing of a PostgreSQL connection"],pg_update:["mixed pg_update(resource db, string table, array fields, array ids[, int options])","Update table using values (field=>value) and ids (id=>value)"],pg_version:["array pg_version([resource connection])","Returns an array with client, protocol and server version (when available)"],php_egg_logo_guid:["string php_egg_logo_guid(void)","Return the special ID used to request the PHP logo in phpinfo screens"],php_ini_loaded_file:["string php_ini_loaded_file(void)","Return the actual loaded ini filename"],php_ini_scanned_files:["string php_ini_scanned_files(void)","Return comma-separated string of .ini files parsed from the additional ini dir"],php_logo_guid:["string php_logo_guid(void)","Return the special ID used to request the PHP logo in phpinfo screens"],php_real_logo_guid:["string php_real_logo_guid(void)","Return the special ID used to request the PHP logo in phpinfo screens"],php_sapi_name:["string php_sapi_name(void)","Return the current SAPI module name"],php_snmpv3:["void php_snmpv3(INTERNAL_FUNCTION_PARAMETERS, int st)","* * Generic SNMPv3 object fetcher * From here is passed on the the common internal object fetcher. * * st=SNMP_CMD_GET snmp3_get() - query an agent and return a single value. * st=SNMP_CMD_GETNEXT snmp3_getnext() - query an agent and return the next single value. * st=SNMP_CMD_WALK snmp3_walk() - walk the mib and return a single dimensional array * containing the values. * st=SNMP_CMD_REALWALK snmp3_real_walk() - walk the mib and return an * array of oid,value pairs. * st=SNMP_CMD_SET snmp3_set() - query an agent and set a single value *"],php_strip_whitespace:["string php_strip_whitespace(string file_name)","Return source with stripped comments and whitespace"],php_uname:["string php_uname(void)","Return information about the system PHP was built on"],phpcredits:["void phpcredits([int flag])","Prints the list of people who've contributed to the PHP project"],phpinfo:["void phpinfo([int what])","Output a page of useful information about PHP and the current request"],phpversion:["string phpversion([string extension])","Return the current PHP version"],pi:["float pi(void)","Returns an approximation of pi"],png2wbmp:["bool png2wbmp (string f_org, string f_dest, int d_height, int d_width, int threshold)","Convert PNG image to WBMP image"],popen:["resource popen(string command, string mode)","Execute a command and open either a read or a write pipe to it"],posix_access:["bool posix_access(string file [, int mode])","Determine accessibility of a file (POSIX.1 5.6.3)"],posix_ctermid:["string posix_ctermid(void)","Generate terminal path name (POSIX.1, 4.7.1)"],posix_get_last_error:["int posix_get_last_error(void)","Retrieve the error number set by the last posix function which failed."],posix_getcwd:["string posix_getcwd(void)","Get working directory pathname (POSIX.1, 5.2.2)"],posix_getegid:["int posix_getegid(void)","Get the current effective group id (POSIX.1, 4.2.1)"],posix_geteuid:["int posix_geteuid(void)","Get the current effective user id (POSIX.1, 4.2.1)"],posix_getgid:["int posix_getgid(void)","Get the current group id (POSIX.1, 4.2.1)"],posix_getgrgid:["array posix_getgrgid(long gid)","Group database access (POSIX.1, 9.2.1)"],posix_getgrnam:["array posix_getgrnam(string groupname)","Group database access (POSIX.1, 9.2.1)"],posix_getgroups:["array posix_getgroups(void)","Get supplementary group id's (POSIX.1, 4.2.3)"],posix_getlogin:["string posix_getlogin(void)","Get user name (POSIX.1, 4.2.4)"],posix_getpgid:["int posix_getpgid(void)","Get the process group id of the specified process (This is not a POSIX function, but a SVR4ism, so we compile conditionally)"],posix_getpgrp:["int posix_getpgrp(void)","Get current process group id (POSIX.1, 4.3.1)"],posix_getpid:["int posix_getpid(void)","Get the current process id (POSIX.1, 4.1.1)"],posix_getppid:["int posix_getppid(void)","Get the parent process id (POSIX.1, 4.1.1)"],posix_getpwnam:["array posix_getpwnam(string groupname)","User database access (POSIX.1, 9.2.2)"],posix_getpwuid:["array posix_getpwuid(long uid)","User database access (POSIX.1, 9.2.2)"],posix_getrlimit:["array posix_getrlimit(void)","Get system resource consumption limits (This is not a POSIX function, but a BSDism and a SVR4ism. We compile conditionally)"],posix_getsid:["int posix_getsid(void)","Get process group id of session leader (This is not a POSIX function, but a SVR4ism, so be compile conditionally)"],posix_getuid:["int posix_getuid(void)","Get the current user id (POSIX.1, 4.2.1)"],posix_initgroups:["bool posix_initgroups(string name, int base_group_id)","Calculate the group access list for the user specified in name."],posix_isatty:["bool posix_isatty(int fd)","Determine if filedesc is a tty (POSIX.1, 4.7.1)"],posix_kill:["bool posix_kill(int pid, int sig)","Send a signal to a process (POSIX.1, 3.3.2)"],posix_mkfifo:["bool posix_mkfifo(string pathname, int mode)","Make a FIFO special file (POSIX.1, 5.4.2)"],posix_mknod:["bool posix_mknod(string pathname, int mode [, int major [, int minor]])","Make a special or ordinary file (POSIX.1)"],posix_setegid:["bool posix_setegid(long uid)","Set effective group id"],posix_seteuid:["bool posix_seteuid(long uid)","Set effective user id"],posix_setgid:["bool posix_setgid(int uid)","Set group id (POSIX.1, 4.2.2)"],posix_setpgid:["bool posix_setpgid(int pid, int pgid)","Set process group id for job control (POSIX.1, 4.3.3)"],posix_setsid:["int posix_setsid(void)","Create session and set process group id (POSIX.1, 4.3.2)"],posix_setuid:["bool posix_setuid(long uid)","Set user id (POSIX.1, 4.2.2)"],posix_strerror:["string posix_strerror(int errno)","Retrieve the system error message associated with the given errno."],posix_times:["array posix_times(void)","Get process times (POSIX.1, 4.5.2)"],posix_ttyname:["string posix_ttyname(int fd)","Determine terminal device name (POSIX.1, 4.7.2)"],posix_uname:["array posix_uname(void)","Get system name (POSIX.1, 4.4.1)"],pow:["number pow(number base, number exponent)","Returns base raised to the power of exponent. Returns integer result when possible"],preg_filter:["mixed preg_filter(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])","Perform Perl-style regular expression replacement and only return matches."],preg_grep:["array preg_grep(string regex, array input [, int flags])","Searches array and returns entries which match regex"],preg_last_error:["int preg_last_error()","Returns the error code of the last regexp execution."],preg_match:["int preg_match(string pattern, string subject [, array &subpatterns [, int flags [, int offset]]])","Perform a Perl-style regular expression match"],preg_match_all:["int preg_match_all(string pattern, string subject, array &subpatterns [, int flags [, int offset]])","Perform a Perl-style global regular expression match"],preg_quote:["string preg_quote(string str [, string delim_char])","Quote regular expression characters plus an optional character"],preg_replace:["mixed preg_replace(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])","Perform Perl-style regular expression replacement."],preg_replace_callback:["mixed preg_replace_callback(mixed regex, mixed callback, mixed subject [, int limit [, int &count]])","Perform Perl-style regular expression replacement using replacement callback."],preg_split:["array preg_split(string pattern, string subject [, int limit [, int flags]])","Split string into an array using a perl-style regular expression as a delimiter"],prev:["mixed prev(array array_arg)","Move array argument's internal pointer to the previous element and return it"],print:["int print(string arg)","Output a string"],print_r:["mixed print_r(mixed var [, bool return])","Prints out or returns information about the specified variable"],printf:["int printf(string format [, mixed arg1 [, mixed ...]])","Output a formatted string"],proc_close:["int proc_close(resource process)","close a process opened by proc_open"],proc_get_status:["array proc_get_status(resource process)","get information about a process opened by proc_open"],proc_nice:["bool proc_nice(int priority)","Change the priority of the current process"],proc_open:["resource proc_open(string command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]])","Run a process with more control over it's file descriptors"],proc_terminate:["bool proc_terminate(resource process [, long signal])","kill a process opened by proc_open"],property_exists:["bool property_exists(mixed object_or_class, string property_name)","Checks if the object or class has a property"],pspell_add_to_personal:["bool pspell_add_to_personal(int pspell, string word)","Adds a word to a personal list"],pspell_add_to_session:["bool pspell_add_to_session(int pspell, string word)","Adds a word to the current session"],pspell_check:["bool pspell_check(int pspell, string word)","Returns true if word is valid"],pspell_clear_session:["bool pspell_clear_session(int pspell)","Clears the current session"],pspell_config_create:["int pspell_config_create(string language [, string spelling [, string jargon [, string encoding]]])","Create a new config to be used later to create a manager"],pspell_config_data_dir:["bool pspell_config_data_dir(int conf, string directory)","location of language data files"],pspell_config_dict_dir:["bool pspell_config_dict_dir(int conf, string directory)","location of the main word list"],pspell_config_ignore:["bool pspell_config_ignore(int conf, int ignore)","Ignore words <= n chars"],pspell_config_mode:["bool pspell_config_mode(int conf, long mode)","Select mode for config (PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLERS)"],pspell_config_personal:["bool pspell_config_personal(int conf, string personal)","Use a personal dictionary for this config"],pspell_config_repl:["bool pspell_config_repl(int conf, string repl)","Use a personal dictionary with replacement pairs for this config"],pspell_config_runtogether:["bool pspell_config_runtogether(int conf, bool runtogether)","Consider run-together words as valid components"],pspell_config_save_repl:["bool pspell_config_save_repl(int conf, bool save)","Save replacement pairs when personal list is saved for this config"],pspell_new:["int pspell_new(string language [, string spelling [, string jargon [, string encoding [, int mode]]]])","Load a dictionary"],pspell_new_config:["int pspell_new_config(int config)","Load a dictionary based on the given config"],pspell_new_personal:["int pspell_new_personal(string personal, string language [, string spelling [, string jargon [, string encoding [, int mode]]]])","Load a dictionary with a personal wordlist"],pspell_save_wordlist:["bool pspell_save_wordlist(int pspell)","Saves the current (personal) wordlist"],pspell_store_replacement:["bool pspell_store_replacement(int pspell, string misspell, string correct)","Notify the dictionary of a user-selected replacement"],pspell_suggest:["array pspell_suggest(int pspell, string word)","Returns array of suggestions"],putenv:["bool putenv(string setting)","Set the value of an environment variable"],quoted_printable_decode:["string quoted_printable_decode(string str)","Convert a quoted-printable string to an 8 bit string"],quoted_printable_encode:["string quoted_printable_encode(string str) */",'PHP_FUNCTION(quoted_printable_encode) { char *str, *new_str; int str_len; size_t new_str_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) != SUCCESS) { return; } if (!str_len) { RETURN_EMPTY_STRING(); } new_str = (char *)php_quot_print_encode((unsigned char *)str, (size_t)str_len, &new_str_len); RETURN_STRINGL(new_str, new_str_len, 0); } /* }}}'],quotemeta:["string quotemeta(string str)","Quotes meta characters"],rad2deg:["float rad2deg(float number)","Converts the radian number to the equivalent number in degrees"],rand:["int rand([int min, int max])","Returns a random number"],range:["array range(mixed low, mixed high[, int step])","Create an array containing the range of integers or characters from low to high (inclusive)"],rawurldecode:["string rawurldecode(string str)","Decodes URL-encodes string"],rawurlencode:["string rawurlencode(string str)","URL-encodes string"],readdir:["string readdir([resource dir_handle])","Read directory entry from dir_handle"],readfile:["int readfile(string filename [, bool use_include_path[, resource context]])","Output a file or a URL"],readgzfile:["int readgzfile(string filename [, int use_include_path])","Output a .gz-file"],readline:["string readline([string prompt])","Reads a line"],readline_add_history:["bool readline_add_history(string prompt)","Adds a line to the history"],readline_callback_handler_install:["void readline_callback_handler_install(string prompt, mixed callback)","Initializes the readline callback interface and terminal, prints the prompt and returns immediately"],readline_callback_handler_remove:["bool readline_callback_handler_remove()","Removes a previously installed callback handler and restores terminal settings"],readline_callback_read_char:["void readline_callback_read_char()","Informs the readline callback interface that a character is ready for input"],readline_clear_history:["bool readline_clear_history(void)","Clears the history"],readline_completion_function:["bool readline_completion_function(string funcname)","Readline completion function?"],readline_info:["mixed readline_info([string varname [, string newvalue]])","Gets/sets various internal readline variables."],readline_list_history:["array readline_list_history(void)","Lists the history"],readline_on_new_line:["void readline_on_new_line(void)","Inform readline that the cursor has moved to a new line"],readline_read_history:["bool readline_read_history([string filename])","Reads the history"],readline_redisplay:["void readline_redisplay(void)","Ask readline to redraw the display"],readline_write_history:["bool readline_write_history([string filename])","Writes the history"],readlink:["string readlink(string filename)","Return the target of a symbolic link"],realpath:["string realpath(string path)","Return the resolved path"],realpath_cache_get:["bool realpath_cache_get()","Get current size of realpath cache"],realpath_cache_size:["bool realpath_cache_size()","Get current size of realpath cache"],recode_file:["bool recode_file(string request, resource input, resource output)","Recode file input into file output according to request"],recode_string:["string recode_string(string request, string str)","Recode string str according to request string"],register_shutdown_function:["void register_shutdown_function(string function_name)","Register a user-level function to be called on request termination"],register_tick_function:["bool register_tick_function(string function_name [, mixed arg [, mixed ... ]])","Registers a tick callback function"],rename:["bool rename(string old_name, string new_name[, resource context])","Rename a file"],require:["bool require(string path)","Includes and evaluates the specified file, erroring if the file cannot be included"],require_once:["bool require_once(string path)","Includes and evaluates the specified file, erroring if the file cannot be included"],reset:["mixed reset(array array_arg)","Set array argument's internal pointer to the first element and return it"],restore_error_handler:["void restore_error_handler(void)","Restores the previously defined error handler function"],restore_exception_handler:["void restore_exception_handler(void)","Restores the previously defined exception handler function"],restore_include_path:["void restore_include_path()","Restore the value of the include_path configuration option"],rewind:["bool rewind(resource fp)","Rewind the position of a file pointer"],rewinddir:["void rewinddir([resource dir_handle])","Rewind dir_handle back to the start"],rmdir:["bool rmdir(string dirname[, resource context])","Remove a directory"],round:["float round(float number [, int precision [, int mode]])","Returns the number rounded to specified precision"],rsort:["bool rsort(array &array_arg [, int sort_flags])","Sort an array in reverse order"],rtrim:["string rtrim(string str [, string character_mask])","Removes trailing whitespace"],scandir:["array scandir(string dir [, int sorting_order [, resource context]])","List files & directories inside the specified path"],sem_acquire:["bool sem_acquire(resource id)","Acquires the semaphore with the given id, blocking if necessary"],sem_get:["resource sem_get(int key [, int max_acquire [, int perm [, int auto_release]])","Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously"],sem_release:["bool sem_release(resource id)","Releases the semaphore with the given id"],sem_remove:["bool sem_remove(resource id)","Removes semaphore from Unix systems"],serialize:["string serialize(mixed variable)","Returns a string representation of variable (which can later be unserialized)"],session_cache_expire:["int session_cache_expire([int new_cache_expire])","Return the current cache expire. If new_cache_expire is given, the current cache_expire is replaced with new_cache_expire"],session_cache_limiter:["string session_cache_limiter([string new_cache_limiter])","Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter"],session_decode:["bool session_decode(string data)","Deserializes data and reinitializes the variables"],session_destroy:["bool session_destroy(void)","Destroy the current session and all data associated with it"],session_encode:["string session_encode(void)","Serializes the current setup and returns the serialized representation"],session_get_cookie_params:["array session_get_cookie_params(void)","Return the session cookie parameters"],session_id:["string session_id([string newid])","Return the current session id. If newid is given, the session id is replaced with newid"],session_is_registered:["bool session_is_registered(string varname)","Checks if a variable is registered in session"],session_module_name:["string session_module_name([string newname])","Return the current module name used for accessing session data. If newname is given, the module name is replaced with newname"],session_name:["string session_name([string newname])","Return the current session name. If newname is given, the session name is replaced with newname"],session_regenerate_id:["bool session_regenerate_id([bool delete_old_session])","Update the current session id with a newly generated one. If delete_old_session is set to true, remove the old session."],session_register:["bool session_register(mixed var_names [, mixed ...])","Adds varname(s) to the list of variables which are freezed at the session end"],session_save_path:["string session_save_path([string newname])","Return the current save path passed to module_name. If newname is given, the save path is replaced with newname"],session_set_cookie_params:["void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])","Set session cookie parameters"],session_set_save_handler:["void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc)","Sets user-level functions"],session_start:["bool session_start(void)","Begin session - reinitializes freezed variables, registers browsers etc"],session_unregister:["bool session_unregister(string varname)","Removes varname from the list of variables which are freezed at the session end"],session_unset:["void session_unset(void)","Unset all registered variables"],session_write_close:["void session_write_close(void)","Write session data and end session"],set_error_handler:["string set_error_handler(string error_handler [, int error_types])","Sets a user-defined error handler function. Returns the previously defined error handler, or false on error"],set_exception_handler:["string set_exception_handler(callable exception_handler)","Sets a user-defined exception handler function. Returns the previously defined exception handler, or false on error"],set_include_path:["string set_include_path(string new_include_path)","Sets the include_path configuration option"],set_magic_quotes_runtime:["bool set_magic_quotes_runtime(int new_setting)","Set the current active configuration setting of magic_quotes_runtime and return previous"],set_time_limit:["bool set_time_limit(int seconds)","Sets the maximum time a script can run"],setcookie:["bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])","Send a cookie"],setlocale:["string setlocale(mixed category, string locale [, string ...])","Set locale information"],setrawcookie:["bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])","Send a cookie with no url encoding of the value"],settype:["bool settype(mixed var, string type)","Set the type of the variable"],sha1:["string sha1(string str [, bool raw_output])","Calculate the sha1 hash of a string"],sha1_file:["string sha1_file(string filename [, bool raw_output])","Calculate the sha1 hash of given filename"],shell_exec:["string shell_exec(string cmd)","Execute command via shell and return complete output as string"],shm_attach:["int shm_attach(int key [, int memsize [, int perm]])","Creates or open a shared memory segment"],shm_detach:["bool shm_detach(resource shm_identifier)","Disconnects from shared memory segment"],shm_get_var:["mixed shm_get_var(resource id, int variable_key)","Returns a variable from shared memory"],shm_has_var:["bool shm_has_var(resource id, int variable_key)","Checks whether a specific entry exists"],shm_put_var:["bool shm_put_var(resource shm_identifier, int variable_key, mixed variable)","Inserts or updates a variable in shared memory"],shm_remove:["bool shm_remove(resource shm_identifier)","Removes shared memory from Unix systems"],shm_remove_var:["bool shm_remove_var(resource id, int variable_key)","Removes variable from shared memory"],shmop_close:["void shmop_close (int shmid)","closes a shared memory segment"],shmop_delete:["bool shmop_delete (int shmid)","mark segment for deletion"],shmop_open:["int shmop_open (int key, string flags, int mode, int size)","gets and attaches a shared memory segment"],shmop_read:["string shmop_read (int shmid, int start, int count)","reads from a shm segment"],shmop_size:["int shmop_size (int shmid)","returns the shm size"],shmop_write:["int shmop_write (int shmid, string data, int offset)","writes to a shared memory segment"],shuffle:["bool shuffle(array array_arg)","Randomly shuffle the contents of an array"],similar_text:["int similar_text(string str1, string str2 [, float percent])","Calculates the similarity between two strings"],simplexml_import_dom:["simplemxml_element simplexml_import_dom(domNode node [, string class_name])","Get a simplexml_element object from dom to allow for processing"],simplexml_load_file:["simplemxml_element simplexml_load_file(string filename [, string class_name [, int options [, string ns [, bool is_prefix]]]])","Load a filename and return a simplexml_element object to allow for processing"],simplexml_load_string:["simplemxml_element simplexml_load_string(string data [, string class_name [, int options [, string ns [, bool is_prefix]]]])","Load a string and return a simplexml_element object to allow for processing"],sin:["float sin(float number)","Returns the sine of the number in radians"],sinh:["float sinh(float number)","Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2"],sleep:["void sleep(int seconds)","Delay for a given number of seconds"],smfi_addheader:["bool smfi_addheader(string headerf, string headerv)","Adds a header to the current message."],smfi_addrcpt:["bool smfi_addrcpt(string rcpt)","Add a recipient to the message envelope."],smfi_chgheader:["bool smfi_chgheader(string headerf, string headerv)","Changes a header's value for the current message."],smfi_delrcpt:["bool smfi_delrcpt(string rcpt)","Removes the named recipient from the current message's envelope."],smfi_getsymval:["string smfi_getsymval(string macro)","Returns the value of the given macro or NULL if the macro is not defined."],smfi_replacebody:["bool smfi_replacebody(string body)","Replaces the body of the current message. If called more than once, subsequent calls result in data being appended to the new body."],smfi_setflags:["void smfi_setflags(long flags)","Sets the flags describing the actions the filter may take."],smfi_setreply:["bool smfi_setreply(string rcode, string xcode, string message)","Directly set the SMTP error reply code for this connection. This code will be used on subsequent error replies resulting from actions taken by this filter."],smfi_settimeout:["void smfi_settimeout(long timeout)","Sets the number of seconds libmilter will wait for an MTA connection before timing out a socket."],snmp2_get:["string snmp2_get(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmp2_getnext:["string snmp2_getnext(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmp2_real_walk:["array snmp2_real_walk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects including their respective object id withing the specified one"],snmp2_set:["int snmp2_set(string host, string community, string object_id, string type, mixed value [, int timeout [, int retries]])","Set the value of a SNMP object"],snmp2_walk:["array snmp2_walk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects under the specified object id"],snmp3_get:["int snmp3_get(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_getnext:["int snmp3_getnext(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_real_walk:["int snmp3_real_walk(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_set:["int snmp3_set(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id, string type, mixed value [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp3_walk:["int snmp3_walk(string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]])","Fetch the value of a SNMP object"],snmp_get_quick_print:["bool snmp_get_quick_print(void)","Return the current status of quick_print"],snmp_get_valueretrieval:["int snmp_get_valueretrieval()","Return the method how the SNMP values will be returned"],snmp_read_mib:["int snmp_read_mib(string filename)","Reads and parses a MIB file into the active MIB tree."],snmp_set_enum_print:["void snmp_set_enum_print(int enum_print)","Return all values that are enums with their enum value instead of the raw integer"],snmp_set_oid_output_format:["void snmp_set_oid_output_format(int oid_format)","Set the OID output format."],snmp_set_quick_print:["void snmp_set_quick_print(int quick_print)","Return all objects including their respective object id withing the specified one"],snmp_set_valueretrieval:["void snmp_set_valueretrieval(int method)","Specify the method how the SNMP values will be returned"],snmpget:["string snmpget(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmpgetnext:["string snmpgetnext(string host, string community, string object_id [, int timeout [, int retries]])","Fetch a SNMP object"],snmprealwalk:["array snmprealwalk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects including their respective object id withing the specified one"],snmpset:["int snmpset(string host, string community, string object_id, string type, mixed value [, int timeout [, int retries]])","Set the value of a SNMP object"],snmpwalk:["array snmpwalk(string host, string community, string object_id [, int timeout [, int retries]])","Return all objects under the specified object id"],socket_accept:["resource socket_accept(resource socket)","Accepts a connection on the listening socket fd"],socket_bind:["bool socket_bind(resource socket, string addr [, int port])","Binds an open socket to a listening port, port is only specified in AF_INET family."],socket_clear_error:["void socket_clear_error([resource socket])","Clears the error on the socket or the last error code."],socket_close:["void socket_close(resource socket)","Closes a file descriptor"],socket_connect:["bool socket_connect(resource socket, string addr [, int port])","Opens a connection to addr:port on the socket specified by socket"],socket_create:["resource socket_create(int domain, int type, int protocol)","Creates an endpoint for communication in the domain specified by domain, of type specified by type"],socket_create_listen:["resource socket_create_listen(int port[, int backlog])","Opens a socket on port to accept connections"],socket_create_pair:["bool socket_create_pair(int domain, int type, int protocol, array &fd)","Creates a pair of indistinguishable sockets and stores them in fds."],socket_get_option:["mixed socket_get_option(resource socket, int level, int optname)","Gets socket options for the socket"],socket_getpeername:["bool socket_getpeername(resource socket, string &addr[, int &port])","Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type."],socket_getsockname:["bool socket_getsockname(resource socket, string &addr[, int &port])","Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type."],socket_last_error:["int socket_last_error([resource socket])","Returns the last socket error (either the last used or the provided socket resource)"],socket_listen:["bool socket_listen(resource socket[, int backlog])","Sets the maximum number of connections allowed to be waited for on the socket specified by fd"],socket_read:["string socket_read(resource socket, int length [, int type])","Reads a maximum of length bytes from socket"],socket_recv:["int socket_recv(resource socket, string &buf, int len, int flags)","Receives data from a connected socket"],socket_recvfrom:["int socket_recvfrom(resource socket, string &buf, int len, int flags, string &name [, int &port])","Receives data from a socket, connected or not"],socket_select:["int socket_select(array &read_fds, array &write_fds, array &except_fds, int tv_sec[, int tv_usec])","Runs the select() system call on the sets mentioned with a timeout specified by tv_sec and tv_usec"],socket_send:["int socket_send(resource socket, string buf, int len, int flags)","Sends data to a connected socket"],socket_sendto:["int socket_sendto(resource socket, string buf, int len, int flags, string addr [, int port])","Sends a message to a socket, whether it is connected or not"],socket_set_block:["bool socket_set_block(resource socket)","Sets blocking mode on a socket resource"],socket_set_nonblock:["bool socket_set_nonblock(resource socket)","Sets nonblocking mode on a socket resource"],socket_set_option:["bool socket_set_option(resource socket, int level, int optname, int|array optval)","Sets socket options for the socket"],socket_shutdown:["bool socket_shutdown(resource socket[, int how])","Shuts down a socket for receiving, sending, or both."],socket_strerror:["string socket_strerror(int errno)","Returns a string describing an error"],socket_write:["int socket_write(resource socket, string buf[, int length])","Writes the buffer to the socket resource, length is optional"],solid_fetch_prev:["bool solid_fetch_prev(resource result_id)",""],sort:["bool sort(array &array_arg [, int sort_flags])","Sort an array"],soundex:["string soundex(string str)","Calculate the soundex key of a string"],spl_autoload:["void spl_autoload(string class_name [, string file_extensions])","Default implementation for __autoload()"],spl_autoload_call:["void spl_autoload_call(string class_name)","Try all registerd autoload function to load the requested class"],spl_autoload_extensions:["string spl_autoload_extensions([string file_extensions])","Register and return default file extensions for spl_autoload"],spl_autoload_functions:["false|array spl_autoload_functions()","Return all registered __autoload() functionns"],spl_autoload_register:['bool spl_autoload_register([mixed autoload_function = "spl_autoload" [, throw = true [, prepend]]])',"Register given function as __autoload() implementation"],spl_autoload_unregister:["bool spl_autoload_unregister(mixed autoload_function)","Unregister given function as __autoload() implementation"],spl_classes:["array spl_classes()","Return an array containing the names of all clsses and interfaces defined in SPL"],spl_object_hash:["string spl_object_hash(object obj)","Return hash id for given object"],split:["array split(string pattern, string string [, int limit])","Split string into array by regular expression"],spliti:["array spliti(string pattern, string string [, int limit])","Split string into array by regular expression case-insensitive"],sprintf:["string sprintf(string format [, mixed arg1 [, mixed ...]])","Return a formatted string"],sql_regcase:["string sql_regcase(string string)","Make regular expression for case insensitive match"],sqlite_array_query:["array sqlite_array_query(resource db, string query [ , int result_type [, bool decode_binary]])","Executes a query against a given database and returns an array of arrays."],sqlite_busy_timeout:["void sqlite_busy_timeout(resource db, int ms)","Set busy timeout duration. If ms <= 0, all busy handlers are disabled."],sqlite_changes:["int sqlite_changes(resource db)","Returns the number of rows that were changed by the most recent SQL statement."],sqlite_close:["void sqlite_close(resource db)","Closes an open sqlite database."],sqlite_column:["mixed sqlite_column(resource result, mixed index_or_name [, bool decode_binary])","Fetches a column from the current row of a result set."],sqlite_create_aggregate:["bool sqlite_create_aggregate(resource db, string funcname, mixed step_func, mixed finalize_func[, long num_args])","Registers an aggregate function for queries."],sqlite_create_function:["bool sqlite_create_function(resource db, string funcname, mixed callback[, long num_args])",'Registers a "regular" function for queries.'],sqlite_current:["array sqlite_current(resource result [, int result_type [, bool decode_binary]])","Fetches the current row from a result set as an array."],sqlite_error_string:["string sqlite_error_string(int error_code)","Returns the textual description of an error code."],sqlite_escape_string:["string sqlite_escape_string(string item)","Escapes a string for use as a query parameter."],sqlite_exec:["boolean sqlite_exec(string query, resource db[, string &error_message])","Executes a result-less query against a given database"],sqlite_factory:["object sqlite_factory(string filename [, int mode [, string &error_message]])","Opens a SQLite database and creates an object for it. Will create the database if it does not exist."],sqlite_fetch_all:["array sqlite_fetch_all(resource result [, int result_type [, bool decode_binary]])","Fetches all rows from a result set as an array of arrays."],sqlite_fetch_array:["array sqlite_fetch_array(resource result [, int result_type [, bool decode_binary]])","Fetches the next row from a result set as an array."],sqlite_fetch_column_types:["resource sqlite_fetch_column_types(string table_name, resource db [, int result_type])","Return an array of column types from a particular table."],sqlite_fetch_object:["object sqlite_fetch_object(resource result [, string class_name [, NULL|array ctor_params [, bool decode_binary]]])","Fetches the next row from a result set as an object."],sqlite_fetch_single:["string sqlite_fetch_single(resource result [, bool decode_binary])","Fetches the first column of a result set as a string."],sqlite_field_name:["string sqlite_field_name(resource result, int field_index)","Returns the name of a particular field of a result set."],sqlite_has_prev:["bool sqlite_has_prev(resource result)","* Returns whether a previous row is available."],sqlite_key:["int sqlite_key(resource result)","Return the current row index of a buffered result."],sqlite_last_error:["int sqlite_last_error(resource db)","Returns the error code of the last error for a database."],sqlite_last_insert_rowid:["int sqlite_last_insert_rowid(resource db)","Returns the rowid of the most recently inserted row."],sqlite_libencoding:["string sqlite_libencoding()","Returns the encoding (iso8859 or UTF-8) of the linked SQLite library."],sqlite_libversion:["string sqlite_libversion()","Returns the version of the linked SQLite library."],sqlite_next:["bool sqlite_next(resource result)","Seek to the next row number of a result set."],sqlite_num_fields:["int sqlite_num_fields(resource result)","Returns the number of fields in a result set."],sqlite_num_rows:["int sqlite_num_rows(resource result)","Returns the number of rows in a buffered result set."],sqlite_open:["resource sqlite_open(string filename [, int mode [, string &error_message]])","Opens a SQLite database. Will create the database if it does not exist."],sqlite_popen:["resource sqlite_popen(string filename [, int mode [, string &error_message]])","Opens a persistent handle to a SQLite database. Will create the database if it does not exist."],sqlite_prev:["bool sqlite_prev(resource result)","* Seek to the previous row number of a result set."],sqlite_query:["resource sqlite_query(string query, resource db [, int result_type [, string &error_message]])","Executes a query against a given database and returns a result handle."],sqlite_rewind:["bool sqlite_rewind(resource result)","Seek to the first row number of a buffered result set."],sqlite_seek:["bool sqlite_seek(resource result, int row)","Seek to a particular row number of a buffered result set."],sqlite_single_query:["array sqlite_single_query(resource db, string query [, bool first_row_only [, bool decode_binary]])","Executes a query and returns either an array for one single column or the value of the first row."],sqlite_udf_decode_binary:["string sqlite_udf_decode_binary(string data)","Decode binary encoding on a string parameter passed to an UDF."],sqlite_udf_encode_binary:["string sqlite_udf_encode_binary(string data)","Apply binary encoding (if required) to a string to return from an UDF."],sqlite_unbuffered_query:["resource sqlite_unbuffered_query(string query, resource db [ , int result_type [, string &error_message]])","Executes a query that does not prefetch and buffer all data."],sqlite_valid:["bool sqlite_valid(resource result)","Returns whether more rows are available."],sqrt:["float sqrt(float number)","Returns the square root of the number"],srand:["void srand([int seed])","Seeds random number generator"],sscanf:["mixed sscanf(string str, string format [, string ...])","Implements an ANSI C compatible sscanf"],stat:["array stat(string filename)","Give information about a file"],str_getcsv:["array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])","Parse a CSV string into an array"],str_ireplace:["mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])","Replaces all occurrences of search in haystack with replace / case-insensitive"],str_pad:["string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])","Returns input string padded on the left or right to specified length with pad_string"],str_repeat:["string str_repeat(string input, int mult)","Returns the input string repeat mult times"],str_replace:["mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])","Replaces all occurrences of search in haystack with replace"],str_rot13:["string str_rot13(string str)","Perform the rot13 transform on a string"],str_shuffle:["void str_shuffle(string str)","Shuffles string. One permutation of all possible is created"],str_split:["array str_split(string str [, int split_length])","Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long."],str_word_count:["mixed str_word_count(string str, [int format [, string charlist]])",'Counts the number of words inside a string. If format of 1 is specified, then the function will return an array containing all the words found inside the string. If format of 2 is specified, then the function will return an associated array where the position of the word is the key and the word itself is the value. For the purpose of this function, \'word\' is defined as a locale dependent string containing alphabetic characters, which also may contain, but not start with "\'" and "-" characters.'],strcasecmp:["int strcasecmp(string str1, string str2)","Binary safe case-insensitive string comparison"],strchr:["string strchr(string haystack, string needle)","An alias for strstr"],strcmp:["int strcmp(string str1, string str2)","Binary safe string comparison"],strcoll:["int strcoll(string str1, string str2)","Compares two strings using the current locale"],strcspn:["int strcspn(string str, string mask [, start [, len]])","Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars)"],stream_bucket_append:["void stream_bucket_append(resource brigade, resource bucket)","Append bucket to brigade"],stream_bucket_make_writeable:["object stream_bucket_make_writeable(resource brigade)","Return a bucket object from the brigade for operating on"],stream_bucket_new:["resource stream_bucket_new(resource stream, string buffer)","Create a new bucket for use on the current stream"],stream_bucket_prepend:["void stream_bucket_prepend(resource brigade, resource bucket)","Prepend bucket to brigade"],stream_context_create:["resource stream_context_create([array options[, array params]])","Create a file context and optionally set parameters"],stream_context_get_default:["resource stream_context_get_default([array options])","Get a handle on the default file/stream context and optionally set parameters"],stream_context_get_options:["array stream_context_get_options(resource context|resource stream)","Retrieve options for a stream/wrapper/context"],stream_context_get_params:["array stream_context_get_params(resource context|resource stream)","Get parameters of a file context"],stream_context_set_default:["resource stream_context_set_default(array options)","Set default file/stream context, returns the context as a resource"],stream_context_set_option:["bool stream_context_set_option(resource context|resource stream, string wrappername, string optionname, mixed value)","Set an option for a wrapper"],stream_context_set_params:["bool stream_context_set_params(resource context|resource stream, array options)","Set parameters for a file context"],stream_copy_to_stream:["long stream_copy_to_stream(resource source, resource dest [, long maxlen [, long pos]])","Reads up to maxlen bytes from source stream and writes them to dest stream."],stream_filter_append:["resource stream_filter_append(resource stream, string filtername[, int read_write[, string filterparams]])","Append a filter to a stream"],stream_filter_prepend:["resource stream_filter_prepend(resource stream, string filtername[, int read_write[, string filterparams]])","Prepend a filter to a stream"],stream_filter_register:["bool stream_filter_register(string filtername, string classname)","Registers a custom filter handler class"],stream_filter_remove:["bool stream_filter_remove(resource stream_filter)","Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource"],stream_get_contents:["string stream_get_contents(resource source [, long maxlen [, long offset]])","Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string."],stream_get_filters:["array stream_get_filters(void)","Returns a list of registered filters"],stream_get_line:["string stream_get_line(resource stream, int maxlen [, string ending])","Read up to maxlen bytes from a stream or until the ending string is found"],stream_get_meta_data:["array stream_get_meta_data(resource fp)","Retrieves header/meta data from streams/file pointers"],stream_get_transports:["array stream_get_transports()","Retrieves list of registered socket transports"],stream_get_wrappers:["array stream_get_wrappers()","Retrieves list of registered stream wrappers"],stream_is_local:["bool stream_is_local(resource stream|string url)",""],stream_resolve_include_path:["string stream_resolve_include_path(string filename)","Determine what file will be opened by calls to fopen() with a relative path"],stream_select:["int stream_select(array &read_streams, array &write_streams, array &except_streams, int tv_sec[, int tv_usec])","Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec"],stream_set_blocking:["bool stream_set_blocking(resource socket, int mode)","Set blocking/non-blocking mode on a socket or stream"],stream_set_timeout:["bool stream_set_timeout(resource stream, int seconds [, int microseconds])","Set timeout on stream read to seconds + microseonds"],stream_set_write_buffer:["int stream_set_write_buffer(resource fp, int buffer)","Set file write buffer"],stream_socket_accept:["resource stream_socket_accept(resource serverstream, [ double timeout [, string &peername ]])","Accept a client connection from a server socket"],stream_socket_client:["resource stream_socket_client(string remoteaddress [, long &errcode [, string &errstring [, double timeout [, long flags [, resource context]]]]])","Open a client connection to a remote address"],stream_socket_enable_crypto:["int stream_socket_enable_crypto(resource stream, bool enable [, int cryptokind [, resource sessionstream]])","Enable or disable a specific kind of crypto on the stream"],stream_socket_get_name:["string stream_socket_get_name(resource stream, bool want_peer)","Returns either the locally bound or remote name for a socket stream"],stream_socket_pair:["array stream_socket_pair(int domain, int type, int protocol)","Creates a pair of connected, indistinguishable socket streams"],stream_socket_recvfrom:["string stream_socket_recvfrom(resource stream, long amount [, long flags [, string &remote_addr]])","Receives data from a socket stream"],stream_socket_sendto:["long stream_socket_sendto(resouce stream, string data [, long flags [, string target_addr]])","Send data to a socket stream. If target_addr is specified it must be in dotted quad (or [ipv6]) format"],stream_socket_server:["resource stream_socket_server(string localaddress [, long &errcode [, string &errstring [, long flags [, resource context]]]])","Create a server socket bound to localaddress"],stream_socket_shutdown:["int stream_socket_shutdown(resource stream, int how)","causes all or part of a full-duplex connection on the socket associated with stream to be shut down. If how is SHUT_RD, further receptions will be disallowed. If how is SHUT_WR, further transmissions will be disallowed. If how is SHUT_RDWR, further receptions and transmissions will be disallowed."],stream_supports_lock:["bool stream_supports_lock(resource stream)","Tells wether the stream supports locking through flock()."],stream_wrapper_register:["bool stream_wrapper_register(string protocol, string classname[, integer flags])","Registers a custom URL protocol handler class"],stream_wrapper_restore:["bool stream_wrapper_restore(string protocol)","Restore the original protocol handler, overriding if necessary"],stream_wrapper_unregister:["bool stream_wrapper_unregister(string protocol)","Unregister a wrapper for the life of the current request."],strftime:["string strftime(string format [, int timestamp])","Format a local time/date according to locale settings"],strip_tags:["string strip_tags(string str [, string allowable_tags])","Strips HTML and PHP tags from a string"],stripcslashes:["string stripcslashes(string str)","Strips backslashes from a string. Uses C-style conventions"],stripos:["int stripos(string haystack, string needle [, int offset])","Finds position of first occurrence of a string within another, case insensitive"],stripslashes:["string stripslashes(string str)","Strips backslashes from a string"],stristr:["string stristr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another, case insensitive"],strlen:["int strlen(string str)","Get string length"],strnatcasecmp:["int strnatcasecmp(string s1, string s2)","Returns the result of case-insensitive string comparison using 'natural' algorithm"],strnatcmp:["int strnatcmp(string s1, string s2)","Returns the result of string comparison using 'natural' algorithm"],strncasecmp:["int strncasecmp(string str1, string str2, int len)","Binary safe string comparison"],strncmp:["int strncmp(string str1, string str2, int len)","Binary safe string comparison"],strpbrk:["array strpbrk(string haystack, string char_list)","Search a string for any of a set of characters"],strpos:["int strpos(string haystack, string needle [, int offset])","Finds position of first occurrence of a string within another"],strptime:["string strptime(string timestamp, string format)","Parse a time/date generated with strftime()"],strrchr:["string strrchr(string haystack, string needle)","Finds the last occurrence of a character in a string within another"],strrev:["string strrev(string str)","Reverse a string"],strripos:["int strripos(string haystack, string needle [, int offset])","Finds position of last occurrence of a string within another string"],strrpos:["int strrpos(string haystack, string needle [, int offset])","Finds position of last occurrence of a string within another string"],strspn:["int strspn(string str, string mask [, start [, len]])","Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars)"],strstr:["string strstr(string haystack, string needle[, bool part])","Finds first occurrence of a string within another"],strtok:["string strtok([string str,] string token)","Tokenize a string"],strtolower:["string strtolower(string str)","Makes a string lowercase"],strtotime:["int strtotime(string time [, int now ])","Convert string representation of date and time to a timestamp"],strtoupper:["string strtoupper(string str)","Makes a string uppercase"],strtr:["string strtr(string str, string from[, string to])","Translates characters in str using given translation tables"],strval:["string strval(mixed var)","Get the string value of a variable"],substr:["string substr(string str, int start [, int length])","Returns part of a string"],substr_compare:["int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])","Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters"],substr_count:["int substr_count(string haystack, string needle [, int offset [, int length]])","Returns the number of times a substring occurs in the string"],substr_replace:["mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length])","Replaces part of a string with another string"],sybase_affected_rows:["int sybase_affected_rows([resource link_id])","Get number of affected rows in last query"],sybase_close:["bool sybase_close([resource link_id])","Close Sybase connection"],sybase_connect:["int sybase_connect([string host [, string user [, string password [, string charset [, string appname [, bool new]]]]]])","Open Sybase server connection"],sybase_data_seek:["bool sybase_data_seek(resource result, int offset)","Move internal row pointer"],sybase_deadlock_retry_count:["void sybase_deadlock_retry_count(int retry_count)","Sets deadlock retry count"],sybase_fetch_array:["array sybase_fetch_array(resource result)","Fetch row as array"],sybase_fetch_assoc:["array sybase_fetch_assoc(resource result)","Fetch row as array without numberic indices"],sybase_fetch_field:["object sybase_fetch_field(resource result [, int offset])","Get field information"],sybase_fetch_object:["object sybase_fetch_object(resource result [, mixed object])","Fetch row as object"],sybase_fetch_row:["array sybase_fetch_row(resource result)","Get row as enumerated array"],sybase_field_seek:["bool sybase_field_seek(resource result, int offset)","Set field offset"],sybase_free_result:["bool sybase_free_result(resource result)","Free result memory"],sybase_get_last_message:["string sybase_get_last_message(void)","Returns the last message from server (over min_message_severity)"],sybase_min_client_severity:["void sybase_min_client_severity(int severity)","Sets minimum client severity"],sybase_min_server_severity:["void sybase_min_server_severity(int severity)","Sets minimum server severity"],sybase_num_fields:["int sybase_num_fields(resource result)","Get number of fields in result"],sybase_num_rows:["int sybase_num_rows(resource result)","Get number of rows in result"],sybase_pconnect:["int sybase_pconnect([string host [, string user [, string password [, string charset [, string appname]]]]])","Open persistent Sybase connection"],sybase_query:["int sybase_query(string query [, resource link_id])","Send Sybase query"],sybase_result:["string sybase_result(resource result, int row, mixed field)","Get result data"],sybase_select_db:["bool sybase_select_db(string database [, resource link_id])","Select Sybase database"],sybase_set_message_handler:["bool sybase_set_message_handler(mixed error_func [, resource connection])","Set the error handler, to be called when a server message is raised. If error_func is NULL the handler will be deleted"],sybase_unbuffered_query:["int sybase_unbuffered_query(string query [, resource link_id])","Send Sybase query"],symlink:["int symlink(string target, string link)","Create a symbolic link"],sys_get_temp_dir:["string sys_get_temp_dir()","Returns directory path used for temporary files"],sys_getloadavg:["array sys_getloadavg()",""],syslog:["bool syslog(int priority, string message)","Generate a system log message"],system:["int system(string command [, int &return_value])","Execute an external program and display output"],tan:["float tan(float number)","Returns the tangent of the number in radians"],tanh:["float tanh(float number)","Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number)"],tempnam:["string tempnam(string dir, string prefix)","Create a unique filename in a directory"],textdomain:["string textdomain(string domain)",'Set the textdomain to "domain". Returns the current domain'],tidy_access_count:["int tidy_access_count()","Returns the Number of Tidy accessibility warnings encountered for specified document."],tidy_clean_repair:["boolean tidy_clean_repair()","Execute configured cleanup and repair operations on parsed markup"],tidy_config_count:["int tidy_config_count()","Returns the Number of Tidy configuration errors encountered for specified document."],tidy_diagnose:["boolean tidy_diagnose()","Run configured diagnostics on parsed and repaired markup."],tidy_error_count:["int tidy_error_count()","Returns the Number of Tidy errors encountered for specified document."],tidy_get_body:["TidyNode tidy_get_body(resource tidy)","Returns a TidyNode Object starting from the tag of the tidy parse tree"],tidy_get_config:["array tidy_get_config()","Get current Tidy configuarion"],tidy_get_error_buffer:["string tidy_get_error_buffer([boolean detailed])","Return warnings and errors which occured parsing the specified document"],tidy_get_head:["TidyNode tidy_get_head()","Returns a TidyNode Object starting from the tag of the tidy parse tree"],tidy_get_html:["TidyNode tidy_get_html()","Returns a TidyNode Object starting from the tag of the tidy parse tree"],tidy_get_html_ver:["int tidy_get_html_ver()","Get the Detected HTML version for the specified document."],tidy_get_opt_doc:["string tidy_get_opt_doc(tidy resource, string optname)","Returns the documentation for the given option name"],tidy_get_output:["string tidy_get_output()","Return a string representing the parsed tidy markup"],tidy_get_release:["string tidy_get_release()","Get release date (version) for Tidy library"],tidy_get_root:["TidyNode tidy_get_root()","Returns a TidyNode Object representing the root of the tidy parse tree"],tidy_get_status:["int tidy_get_status()","Get status of specfied document."],tidy_getopt:["mixed tidy_getopt(string option)","Returns the value of the specified configuration option for the tidy document."],tidy_is_xhtml:["boolean tidy_is_xhtml()","Indicates if the document is a XHTML document."],tidy_is_xml:["boolean tidy_is_xml()","Indicates if the document is a generic (non HTML/XHTML) XML document."],tidy_parse_file:["boolean tidy_parse_file(string file [, mixed config_options [, string encoding [, bool use_include_path]]])","Parse markup in file or URI"],tidy_parse_string:["bool tidy_parse_string(string input [, mixed config_options [, string encoding]])","Parse a document stored in a string"],tidy_repair_file:["boolean tidy_repair_file(string filename [, mixed config_file [, string encoding [, bool use_include_path]]])","Repair a file using an optionally provided configuration file"],tidy_repair_string:["boolean tidy_repair_string(string data [, mixed config_file [, string encoding]])","Repair a string using an optionally provided configuration file"],tidy_warning_count:["int tidy_warning_count()","Returns the Number of Tidy warnings encountered for specified document."],time:["int time(void)","Return current UNIX timestamp"],time_nanosleep:["mixed time_nanosleep(long seconds, long nanoseconds)","Delay for a number of seconds and nano seconds"],time_sleep_until:["mixed time_sleep_until(float timestamp)","Make the script sleep until the specified time"],timezone_abbreviations_list:["array timezone_abbreviations_list()","Returns associative array containing dst, offset and the timezone name"],timezone_identifiers_list:["array timezone_identifiers_list([long what[, string country]])","Returns numerically index array with all timezone identifiers."],timezone_location_get:["array timezone_location_get()","Returns location information for a timezone, including country code, latitude/longitude and comments"],timezone_name_from_abbr:["string timezone_name_from_abbr(string abbr[, long gmtOffset[, long isdst]])","Returns the timezone name from abbrevation"],timezone_name_get:["string timezone_name_get(DateTimeZone object)","Returns the name of the timezone."],timezone_offset_get:["long timezone_offset_get(DateTimeZone object, DateTime object)","Returns the timezone offset."],timezone_open:["DateTimeZone timezone_open(string timezone)","Returns new DateTimeZone object"],timezone_transitions_get:["array timezone_transitions_get(DateTimeZone object [, long timestamp_begin [, long timestamp_end ]])","Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone."],timezone_version_get:["array timezone_version_get()","Returns the Olson database version number."],tmpfile:["resource tmpfile(void)","Create a temporary file that will be deleted automatically after use"],token_get_all:["array token_get_all(string source)",""],token_name:["string token_name(int type)",""],touch:["bool touch(string filename [, int time [, int atime]])","Set modification time of file"],trigger_error:["void trigger_error(string messsage [, int error_type])","Generates a user-level error/warning/notice message"],trim:["string trim(string str [, string character_mask])","Strips whitespace from the beginning and end of a string"],uasort:["bool uasort(array array_arg, string cmp_function)","Sort an array with a user-defined comparison function and maintain index association"],ucfirst:["string ucfirst(string str)","Make a string's first character lowercase"],ucwords:["string ucwords(string str)","Uppercase the first character of every word in a string"],uksort:["bool uksort(array array_arg, string cmp_function)","Sort an array by keys using a user-defined comparison function"],umask:["int umask([int mask])","Return or change the umask"],uniqid:["string uniqid([string prefix [, bool more_entropy]])","Generates a unique ID"],unixtojd:["int unixtojd([int timestamp])","Convert UNIX timestamp to Julian Day"],unlink:["bool unlink(string filename[, context context])","Delete a file"],unpack:["array unpack(string format, string input)","Unpack binary string into named array elements according to format argument"],unregister_tick_function:["void unregister_tick_function(string function_name)","Unregisters a tick callback function"],unserialize:["mixed unserialize(string variable_representation)","Takes a string representation of variable and recreates it"],unset:["void unset (mixed var [, mixed var])","Unset a given variable"],urldecode:["string urldecode(string str)","Decodes URL-encoded string"],urlencode:["string urlencode(string str)","URL-encodes string"],usleep:["void usleep(int micro_seconds)","Delay for a given number of micro seconds"],usort:["bool usort(array array_arg, string cmp_function)","Sort an array by values using a user-defined comparison function"],utf8_decode:["string utf8_decode(string data)","Converts a UTF-8 encoded string to ISO-8859-1"],utf8_encode:["string utf8_encode(string data)","Encodes an ISO-8859-1 string to UTF-8"],var_dump:["void var_dump(mixed var)","Dumps a string representation of variable to output"],var_export:["mixed var_export(mixed var [, bool return])","Outputs or returns a string representation of a variable"],variant_abs:["mixed variant_abs(mixed left)","Returns the absolute value of a variant"],variant_add:["mixed variant_add(mixed left, mixed right)",'"Adds" two variant values together and returns the result'],variant_and:["mixed variant_and(mixed left, mixed right)","performs a bitwise AND operation between two variants and returns the result"],variant_cast:["object variant_cast(object variant, int type)","Convert a variant into a new variant object of another type"],variant_cat:["mixed variant_cat(mixed left, mixed right)","concatenates two variant values together and returns the result"],variant_cmp:["int variant_cmp(mixed left, mixed right [, int lcid [, int flags]])","Compares two variants"],variant_date_from_timestamp:["object variant_date_from_timestamp(int timestamp)","Returns a variant date representation of a unix timestamp"],variant_date_to_timestamp:["int variant_date_to_timestamp(object variant)","Converts a variant date/time value to unix timestamp"],variant_div:["mixed variant_div(mixed left, mixed right)","Returns the result from dividing two variants"],variant_eqv:["mixed variant_eqv(mixed left, mixed right)","Performs a bitwise equivalence on two variants"],variant_fix:["mixed variant_fix(mixed left)","Returns the integer part ? of a variant"],variant_get_type:["int variant_get_type(object variant)","Returns the VT_XXX type code for a variant"],variant_idiv:["mixed variant_idiv(mixed left, mixed right)","Converts variants to integers and then returns the result from dividing them"],variant_imp:["mixed variant_imp(mixed left, mixed right)","Performs a bitwise implication on two variants"],variant_int:["mixed variant_int(mixed left)","Returns the integer portion of a variant"],variant_mod:["mixed variant_mod(mixed left, mixed right)","Divides two variants and returns only the remainder"],variant_mul:["mixed variant_mul(mixed left, mixed right)","multiplies the values of the two variants and returns the result"],variant_neg:["mixed variant_neg(mixed left)","Performs logical negation on a variant"],variant_not:["mixed variant_not(mixed left)","Performs bitwise not negation on a variant"],variant_or:["mixed variant_or(mixed left, mixed right)","Performs a logical disjunction on two variants"],variant_pow:["mixed variant_pow(mixed left, mixed right)","Returns the result of performing the power function with two variants"],variant_round:["mixed variant_round(mixed left, int decimals)","Rounds a variant to the specified number of decimal places"],variant_set:["void variant_set(object variant, mixed value)","Assigns a new value for a variant object"],variant_set_type:["void variant_set_type(object variant, int type)",'Convert a variant into another type. Variant is modified "in-place"'],variant_sub:["mixed variant_sub(mixed left, mixed right)","subtracts the value of the right variant from the left variant value and returns the result"],variant_xor:["mixed variant_xor(mixed left, mixed right)","Performs a logical exclusion on two variants"],version_compare:["int version_compare(string ver1, string ver2 [, string oper])",'Compares two "PHP-standardized" version number strings'],vfprintf:["int vfprintf(resource stream, string format, array args)","Output a formatted string into a stream"],virtual:["bool virtual(string filename)","Perform an Apache sub-request"],vprintf:["int vprintf(string format, array args)","Output a formatted string"],vsprintf:["string vsprintf(string format, array args)","Return a formatted string"],wddx_add_vars:["int wddx_add_vars(resource packet_id, mixed var_names [, mixed ...])","Serializes given variables and adds them to packet given by packet_id"],wddx_deserialize:["mixed wddx_deserialize(mixed packet)","Deserializes given packet and returns a PHP value"],wddx_packet_end:["string wddx_packet_end(resource packet_id)","Ends specified WDDX packet and returns the string containing the packet"],wddx_packet_start:["resource wddx_packet_start([string comment])","Starts a WDDX packet with optional comment and returns the packet id"],wddx_serialize_value:["string wddx_serialize_value(mixed var [, string comment])","Creates a new packet and serializes the given value"],wddx_serialize_vars:["string wddx_serialize_vars(mixed var_name [, mixed ...])","Creates a new packet and serializes given variables into a struct"],wordwrap:["string wordwrap(string str [, int width [, string break [, boolean cut]]])","Wraps buffer to selected number of characters using string break char"],xml_error_string:["string xml_error_string(int code)","Get XML parser error string"],xml_get_current_byte_index:["int xml_get_current_byte_index(resource parser)","Get current byte index for an XML parser"],xml_get_current_column_number:["int xml_get_current_column_number(resource parser)","Get current column number for an XML parser"],xml_get_current_line_number:["int xml_get_current_line_number(resource parser)","Get current line number for an XML parser"],xml_get_error_code:["int xml_get_error_code(resource parser)","Get XML parser error code"],xml_parse:["int xml_parse(resource parser, string data [, int isFinal])","Start parsing an XML document"],xml_parse_into_struct:["int xml_parse_into_struct(resource parser, string data, array &values [, array &index ])","Parsing a XML document"],xml_parser_create:["resource xml_parser_create([string encoding])","Create an XML parser"],xml_parser_create_ns:["resource xml_parser_create_ns([string encoding [, string sep]])","Create an XML parser"],xml_parser_free:["int xml_parser_free(resource parser)","Free an XML parser"],xml_parser_get_option:["int xml_parser_get_option(resource parser, int option)","Get options from an XML parser"],xml_parser_set_option:["int xml_parser_set_option(resource parser, int option, mixed value)","Set options in an XML parser"],xml_set_character_data_handler:["int xml_set_character_data_handler(resource parser, string hdl)","Set up character data handler"],xml_set_default_handler:["int xml_set_default_handler(resource parser, string hdl)","Set up default handler"],xml_set_element_handler:["int xml_set_element_handler(resource parser, string shdl, string ehdl)","Set up start and end element handlers"],xml_set_end_namespace_decl_handler:["int xml_set_end_namespace_decl_handler(resource parser, string hdl)","Set up character data handler"],xml_set_external_entity_ref_handler:["int xml_set_external_entity_ref_handler(resource parser, string hdl)","Set up external entity reference handler"],xml_set_notation_decl_handler:["int xml_set_notation_decl_handler(resource parser, string hdl)","Set up notation declaration handler"],xml_set_object:["int xml_set_object(resource parser, object &obj)","Set up object which should be used for callbacks"],xml_set_processing_instruction_handler:["int xml_set_processing_instruction_handler(resource parser, string hdl)","Set up processing instruction (PI) handler"],xml_set_start_namespace_decl_handler:["int xml_set_start_namespace_decl_handler(resource parser, string hdl)","Set up character data handler"],xml_set_unparsed_entity_decl_handler:["int xml_set_unparsed_entity_decl_handler(resource parser, string hdl)","Set up unparsed entity declaration handler"],xmlrpc_decode:["array xmlrpc_decode(string xml [, string encoding])","Decodes XML into native PHP types"],xmlrpc_decode_request:["array xmlrpc_decode_request(string xml, string& method [, string encoding])","Decodes XML into native PHP types"],xmlrpc_encode:["string xmlrpc_encode(mixed value)","Generates XML for a PHP value"],xmlrpc_encode_request:["string xmlrpc_encode_request(string method, mixed params [, array output_options])","Generates XML for a method request"],xmlrpc_get_type:["string xmlrpc_get_type(mixed value)","Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings"],xmlrpc_is_fault:["bool xmlrpc_is_fault(array)","Determines if an array value represents an XMLRPC fault."],xmlrpc_parse_method_descriptions:["array xmlrpc_parse_method_descriptions(string xml)","Decodes XML into a list of method descriptions"],xmlrpc_server_add_introspection_data:["int xmlrpc_server_add_introspection_data(resource server, array desc)","Adds introspection documentation"],xmlrpc_server_call_method:["mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])","Parses XML requests and call methods"],xmlrpc_server_create:["resource xmlrpc_server_create(void)","Creates an xmlrpc server"],xmlrpc_server_destroy:["int xmlrpc_server_destroy(resource server)","Destroys server resources"],xmlrpc_server_register_introspection_callback:["bool xmlrpc_server_register_introspection_callback(resource server, string function)","Register a PHP function to generate documentation"],xmlrpc_server_register_method:["bool xmlrpc_server_register_method(resource server, string method_name, string function)","Register a PHP function to handle method matching method_name"],xmlrpc_set_type:["bool xmlrpc_set_type(string value, string type)","Sets xmlrpc type, base64 or datetime, for a PHP string value"],xmlwriter_end_attribute:["bool xmlwriter_end_attribute(resource xmlwriter)","End attribute - returns FALSE on error"],xmlwriter_end_cdata:["bool xmlwriter_end_cdata(resource xmlwriter)","End current CDATA - returns FALSE on error"],xmlwriter_end_comment:["bool xmlwriter_end_comment(resource xmlwriter)","Create end comment - returns FALSE on error"],xmlwriter_end_document:["bool xmlwriter_end_document(resource xmlwriter)","End current document - returns FALSE on error"],xmlwriter_end_dtd:["bool xmlwriter_end_dtd(resource xmlwriter)","End current DTD - returns FALSE on error"],xmlwriter_end_dtd_attlist:["bool xmlwriter_end_dtd_attlist(resource xmlwriter)","End current DTD AttList - returns FALSE on error"],xmlwriter_end_dtd_element:["bool xmlwriter_end_dtd_element(resource xmlwriter)","End current DTD element - returns FALSE on error"],xmlwriter_end_dtd_entity:["bool xmlwriter_end_dtd_entity(resource xmlwriter)","End current DTD Entity - returns FALSE on error"],xmlwriter_end_element:["bool xmlwriter_end_element(resource xmlwriter)","End current element - returns FALSE on error"],xmlwriter_end_pi:["bool xmlwriter_end_pi(resource xmlwriter)","End current PI - returns FALSE on error"],xmlwriter_flush:["mixed xmlwriter_flush(resource xmlwriter [,bool empty])","Output current buffer"],xmlwriter_full_end_element:["bool xmlwriter_full_end_element(resource xmlwriter)","End current element - returns FALSE on error"],xmlwriter_open_memory:["resource xmlwriter_open_memory()","Create new xmlwriter using memory for string output"],xmlwriter_open_uri:["resource xmlwriter_open_uri(resource xmlwriter, string source)","Create new xmlwriter using source uri for output"],xmlwriter_output_memory:["string xmlwriter_output_memory(resource xmlwriter [,bool flush])","Output current buffer as string"],xmlwriter_set_indent:["bool xmlwriter_set_indent(resource xmlwriter, bool indent)","Toggle indentation on/off - returns FALSE on error"],xmlwriter_set_indent_string:["bool xmlwriter_set_indent_string(resource xmlwriter, string indentString)","Set string used for indenting - returns FALSE on error"],xmlwriter_start_attribute:["bool xmlwriter_start_attribute(resource xmlwriter, string name)","Create start attribute - returns FALSE on error"],xmlwriter_start_attribute_ns:["bool xmlwriter_start_attribute_ns(resource xmlwriter, string prefix, string name, string uri)","Create start namespaced attribute - returns FALSE on error"],xmlwriter_start_cdata:["bool xmlwriter_start_cdata(resource xmlwriter)","Create start CDATA tag - returns FALSE on error"],xmlwriter_start_comment:["bool xmlwriter_start_comment(resource xmlwriter)","Create start comment - returns FALSE on error"],xmlwriter_start_document:["bool xmlwriter_start_document(resource xmlwriter, string version, string encoding, string standalone)","Create document tag - returns FALSE on error"],xmlwriter_start_dtd:["bool xmlwriter_start_dtd(resource xmlwriter, string name, string pubid, string sysid)","Create start DTD tag - returns FALSE on error"],xmlwriter_start_dtd_attlist:["bool xmlwriter_start_dtd_attlist(resource xmlwriter, string name)","Create start DTD AttList - returns FALSE on error"],xmlwriter_start_dtd_element:["bool xmlwriter_start_dtd_element(resource xmlwriter, string name)","Create start DTD element - returns FALSE on error"],xmlwriter_start_dtd_entity:["bool xmlwriter_start_dtd_entity(resource xmlwriter, string name, bool isparam)","Create start DTD Entity - returns FALSE on error"],xmlwriter_start_element:["bool xmlwriter_start_element(resource xmlwriter, string name)","Create start element tag - returns FALSE on error"],xmlwriter_start_element_ns:["bool xmlwriter_start_element_ns(resource xmlwriter, string prefix, string name, string uri)","Create start namespaced element tag - returns FALSE on error"],xmlwriter_start_pi:["bool xmlwriter_start_pi(resource xmlwriter, string target)","Create start PI tag - returns FALSE on error"],xmlwriter_text:["bool xmlwriter_text(resource xmlwriter, string content)","Write text - returns FALSE on error"],xmlwriter_write_attribute:["bool xmlwriter_write_attribute(resource xmlwriter, string name, string content)","Write full attribute - returns FALSE on error"],xmlwriter_write_attribute_ns:["bool xmlwriter_write_attribute_ns(resource xmlwriter, string prefix, string name, string uri, string content)","Write full namespaced attribute - returns FALSE on error"],xmlwriter_write_cdata:["bool xmlwriter_write_cdata(resource xmlwriter, string content)","Write full CDATA tag - returns FALSE on error"],xmlwriter_write_comment:["bool xmlwriter_write_comment(resource xmlwriter, string content)","Write full comment tag - returns FALSE on error"],xmlwriter_write_dtd:["bool xmlwriter_write_dtd(resource xmlwriter, string name, string pubid, string sysid, string subset)","Write full DTD tag - returns FALSE on error"],xmlwriter_write_dtd_attlist:["bool xmlwriter_write_dtd_attlist(resource xmlwriter, string name, string content)","Write full DTD AttList tag - returns FALSE on error"],xmlwriter_write_dtd_element:["bool xmlwriter_write_dtd_element(resource xmlwriter, string name, string content)","Write full DTD element tag - returns FALSE on error"],xmlwriter_write_dtd_entity:["bool xmlwriter_write_dtd_entity(resource xmlwriter, string name, string content [, int pe [, string pubid [, string sysid [, string ndataid]]]])","Write full DTD Entity tag - returns FALSE on error"],xmlwriter_write_element:["bool xmlwriter_write_element(resource xmlwriter, string name[, string content])","Write full element tag - returns FALSE on error"],xmlwriter_write_element_ns:["bool xmlwriter_write_element_ns(resource xmlwriter, string prefix, string name, string uri[, string content])","Write full namesapced element tag - returns FALSE on error"],xmlwriter_write_pi:["bool xmlwriter_write_pi(resource xmlwriter, string target, string content)","Write full PI tag - returns FALSE on error"],xmlwriter_write_raw:["bool xmlwriter_write_raw(resource xmlwriter, string content)","Write text - returns FALSE on error"],xsl_xsltprocessor_get_parameter:["string xsl_xsltprocessor_get_parameter(string namespace, string name);",""],xsl_xsltprocessor_has_exslt_support:["bool xsl_xsltprocessor_has_exslt_support();",""],xsl_xsltprocessor_import_stylesheet:["void xsl_xsltprocessor_import_stylesheet(domdocument doc);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# Since:"],xsl_xsltprocessor_register_php_functions:["void xsl_xsltprocessor_register_php_functions([mixed $restrict]);",""],xsl_xsltprocessor_remove_parameter:["bool xsl_xsltprocessor_remove_parameter(string namespace, string name);",""],xsl_xsltprocessor_set_parameter:["bool xsl_xsltprocessor_set_parameter(string namespace, mixed name [, string value]);",""],xsl_xsltprocessor_set_profiling:["bool xsl_xsltprocessor_set_profiling(string filename) */",'PHP_FUNCTION(xsl_xsltprocessor_set_profiling) { zval *id; xsl_object *intern; char *filename = NULL; int filename_len; DOM_GET_THIS(id); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s!", &filename, &filename_len) == SUCCESS) { intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); if (intern->profiling) { efree(intern->profiling); } if (filename != NULL) { intern->profiling = estrndup(filename,filename_len); } else { intern->profiling = NULL; } RETURN_TRUE; } else { WRONG_PARAM_COUNT; } } /* }}} end xsl_xsltprocessor_set_profiling'],xsl_xsltprocessor_transform_to_doc:["domdocument xsl_xsltprocessor_transform_to_doc(domnode doc);","URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# Since:"],xsl_xsltprocessor_transform_to_uri:["int xsl_xsltprocessor_transform_to_uri(domdocument doc, string uri);",""],xsl_xsltprocessor_transform_to_xml:["string xsl_xsltprocessor_transform_to_xml(domdocument doc);",""],zend_logo_guid:["string zend_logo_guid(void)","Return the special ID used to request the Zend logo in phpinfo screens"],zend_version:["string zend_version(void)","Get the version of the Zend Engine"],zip_close:["void zip_close(resource zip)","Close a Zip archive"],zip_entry_close:["void zip_entry_close(resource zip_ent)","Close a zip entry"],zip_entry_compressedsize:["int zip_entry_compressedsize(resource zip_entry)","Return the compressed size of a ZZip entry"],zip_entry_compressionmethod:["string zip_entry_compressionmethod(resource zip_entry)","Return a string containing the compression method used on a particular entry"],zip_entry_filesize:["int zip_entry_filesize(resource zip_entry)","Return the actual filesize of a ZZip entry"],zip_entry_name:["string zip_entry_name(resource zip_entry)","Return the name given a ZZip entry"],zip_entry_open:["bool zip_entry_open(resource zip_dp, resource zip_entry [, string mode])","Open a Zip File, pointed by the resource entry"],zip_entry_read:["mixed zip_entry_read(resource zip_entry [, int len])","Read from an open directory entry"],zip_open:["resource zip_open(string filename)","Create new zip using source uri for output"],zip_read:["resource zip_read(resource zip)","Returns the next file in the archive"],zlib_get_coding_type:["string zlib_get_coding_type(void)","Returns the coding type used for output compression"]},i={$_COOKIE:{type:"array"},$_ENV:{type:"array"},$_FILES:{type:"array"},$_GET:{type:"array"},$_POST:{type:"array"},$_REQUEST:{type:"array"},$_SERVER:{type:"array",value:{DOCUMENT_ROOT:1,GATEWAY_INTERFACE:1,HTTP_ACCEPT:1,HTTP_ACCEPT_CHARSET:1,HTTP_ACCEPT_ENCODING:1,HTTP_ACCEPT_LANGUAGE:1,HTTP_CONNECTION:1,HTTP_HOST:1,HTTP_REFERER:1,HTTP_USER_AGENT:1,PATH_TRANSLATED:1,PHP_SELF:1,QUERY_STRING:1,REMOTE_ADDR:1,REMOTE_PORT:1,REQUEST_METHOD:1,REQUEST_URI:1,SCRIPT_FILENAME:1,SCRIPT_NAME:1,SERVER_ADMIN:1,SERVER_NAME:1,SERVER_PORT:1,SERVER_PROTOCOL:1,SERVER_SIGNATURE:1,SERVER_SOFTWARE:1}},$_SESSION:{type:"array"},$GLOBALS:{type:"array"}},o=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(i.type==="identifier")return this.getFunctionCompletions(e,t,n,r);if(s(i,"variable"))return this.getVariableCompletions(e,t,n,r);var o=t.getLine(n.row).substr(0,n.column);return i.type==="string"&&/(\$[\w]*)\[["']([^'"]*)$/i.test(o)?this.getArrayKeyCompletions(e,t,n,r):[]},this.getFunctionCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+"($0)",meta:"php function",score:Number.MAX_VALUE,docHTML:r[e][1]}})},this.getVariableCompletions=function(e,t,n,r){var s=Object.keys(i);return s.map(function(e){return{caption:e,value:e,meta:"php variable",score:Number.MAX_VALUE}})},this.getArrayKeyCompletions=function(e,t,n,r){var s=t.getLine(n.row).substr(0,n.column),o=s.match(/(\$[\w]*)\[["']([^'"]*)$/i)[1];if(!i[o])return[];var u=[];return i[o].type==="array"&&i[o].value&&(u=Object.keys(i[o].value)),u.map(function(e){return{caption:e,value:e,meta:"php array key",score:Number.MAX_VALUE}})}}).call(o.prototype),t.PhpCompletions=o}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),define("ace/mode/php",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/php_highlight_rules","ace/mode/php_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/php_completions","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/unicode","ace/mode/html","ace/mode/javascript","ace/mode/css"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./php_highlight_rules").PhpHighlightRules,o=e("./php_highlight_rules").PhpLangHighlightRules,u=e("./matching_brace_outdent").MatchingBraceOutdent,a=e("../range").Range,f=e("../worker/worker_client").WorkerClient,l=e("./php_completions").PhpCompletions,c=e("./behaviour/cstyle").CstyleBehaviour,h=e("./folding/cstyle").FoldMode,p=e("../unicode"),d=e("./html").Mode,v=e("./javascript").Mode,m=e("./css").Mode,g=function(e){this.HighlightRules=o,this.$outdent=new u,this.$behaviour=new c,this.$completer=new l,this.foldingRules=new h};r.inherits(g,i),function(){this.tokenRe=new RegExp("^["+p.packages.L+p.packages.Mn+p.packages.Mc+p.packages.Nd+p.packages.Pc+"_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+p.packages.L+p.packages.Mn+p.packages.Mc+p.packages.Nd+p.packages.Pc+"_]|\\s])+","g"),this.lineCommentStart=["//","#"],this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var u=t.match(/^.*[\{\(\[:]\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o!="doc-start")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.$id="ace/mode/php-inline"}.call(g.prototype);var y=function(e){if(e&&e.inline){var t=new g;return t.createWorker=this.createWorker,t.inlinePhp=!0,t}d.call(this),this.HighlightRules=s,this.createModeDelegates({"js-":v,"css-":m,"php-":g}),this.foldingRules.subModes["php-"]=new h};r.inherits(y,d),function(){this.createWorker=function(e){var t=new f(["ace"],"ace/mode/php_worker","PhpWorker");return t.attachToDocument(e.getDocument()),this.inlinePhp&&t.call("setOptions",[{inline:!0}]),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/php"}.call(y.prototype),t.Mode=y}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-plain_text.js b/public/themes/pterodactyl/vendor/ace/mode-plain_text.js deleted file mode 100644 index 2e2cfb44a..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-plain_text.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,u=function(){this.HighlightRules=s,this.$behaviour=new o};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return""},this.$id="ace/mode/plain_text"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-properties.js b/public/themes/pterodactyl/vendor/ace/mode-properties.js deleted file mode 100644 index 1f77ca3af..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-properties.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/properties_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=/\\u[0-9a-fA-F]{4}|\\/;this.$rules={start:[{token:"comment",regex:/[!#].*$/},{token:"keyword",regex:/[=:]$/},{token:"keyword",regex:/[=:]/,next:"value"},{token:"constant.language.escape",regex:e},{defaultToken:"variable"}],value:[{regex:/\\$/,token:"string",next:"value"},{regex:/$/,token:"string",next:"start"},{token:"constant.language.escape",regex:e},{defaultToken:"string"}]}};r.inherits(s,i),t.PropertiesHighlightRules=s}),define("ace/mode/properties",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/properties_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./properties_highlight_rules").PropertiesHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.$id="ace/mode/properties"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-python.js b/public/themes/pterodactyl/vendor/ace/mode-python.js deleted file mode 100644 index f4e6c0d69..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-python.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/python_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield",t="True|False|None|NotImplemented|Ellipsis|__debug__",n="abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|float|list|raw_input|unichr|callable|format|locals|reduce|unicode|chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|__import__|complex|hash|min|set|apply|delattr|help|next|setattr|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern",r=this.createKeywordMapper({"invalid.deprecated":"debugger","support.function":n,"constant.language":t,keyword:e},"identifier"),i="(?:r|u|ur|R|U|UR|Ur|uR)?",s="(?:(?:[1-9]\\d*)|(?:0))",o="(?:0[oO]?[0-7]+)",u="(?:0[xX][\\dA-Fa-f]+)",a="(?:0[bB][01]+)",f="(?:"+s+"|"+o+"|"+u+"|"+a+")",l="(?:[eE][+-]?\\d+)",c="(?:\\.\\d+)",h="(?:\\d+)",p="(?:(?:"+h+"?"+c+")|(?:"+h+"\\.))",d="(?:(?:"+p+"|"+h+")"+l+")",v="(?:"+d+"|"+p+")",m="\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})";this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string",regex:i+'"{3}',next:"qqstring3"},{token:"string",regex:i+'"(?=.)',next:"qqstring"},{token:"string",regex:i+"'{3}",next:"qstring3"},{token:"string",regex:i+"'(?=.)",next:"qstring"},{token:"constant.numeric",regex:"(?:"+v+"|\\d+)[jJ]\\b"},{token:"constant.numeric",regex:v},{token:"constant.numeric",regex:f+"[lL]\\b"},{token:"constant.numeric",regex:f+"\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}],qqstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],qstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],qqstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.PythonHighlightRules=s}),define("ace/mode/folding/pythonic",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){this.foldingStartMarker=new RegExp("([\\[{])(?:\\s*)$|("+e+")(?:\\s*)(?:#.*)?$")};r.inherits(s,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i)return i[1]?this.openingBracketBlock(e,i[1],n,i.index):i[2]?this.indentationBlock(e,n,i.index+i[2].length):this.indentationBlock(e,n)}}.call(s.prototype)}),define("ace/mode/python",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/python_highlight_rules","ace/mode/folding/pythonic","ace/range"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./python_highlight_rules").PythonHighlightRules,o=e("./folding/pythonic").FoldMode,u=e("../range").Range,a=function(){this.HighlightRules=s,this.foldingRules=new o("\\:"),this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.getTokenizer().getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new u(n,r.length-i.length,n,r.length))},this.$id="ace/mode/python"}.call(a.prototype),t.Mode=a}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-ruby.js b/public/themes/pterodactyl/vendor/ace/mode-ruby.js deleted file mode 100644 index b857c1fd0..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-ruby.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/ruby_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.constantOtherSymbol={token:"constant.other.symbol.ruby",regex:"[:](?:[A-Za-z_]|[@$](?=[a-zA-Z0-9_]))[a-zA-Z0-9_]*[!=?]?"},o=t.qString={token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},u=t.qqString={token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},a=t.tString={token:"string",regex:"[`](?:(?:\\\\.)|(?:[^'\\\\]))*?[`]"},f=t.constantNumericHex={token:"constant.numeric",regex:"0[xX][0-9a-fA-F](?:[0-9a-fA-F]|_(?=[0-9a-fA-F]))*\\b"},l=t.constantNumericFloat={token:"constant.numeric",regex:"[+-]?\\d(?:\\d|_(?=\\d))*(?:(?:\\.\\d(?:\\d|_(?=\\d))*)?(?:[eE][+-]?\\d+)?)?\\b"},c=function(){var e="abort|Array|assert|assert_equal|assert_not_equal|assert_same|assert_not_same|assert_nil|assert_not_nil|assert_match|assert_no_match|assert_in_delta|assert_throws|assert_raise|assert_nothing_raised|assert_instance_of|assert_kind_of|assert_respond_to|assert_operator|assert_send|assert_difference|assert_no_difference|assert_recognizes|assert_generates|assert_response|assert_redirected_to|assert_template|assert_select|assert_select_email|assert_select_rjs|assert_select_encoded|css_select|at_exit|attr|attr_writer|attr_reader|attr_accessor|attr_accessible|autoload|binding|block_given?|callcc|caller|catch|chomp|chomp!|chop|chop!|defined?|delete_via_redirect|eval|exec|exit|exit!|fail|Float|flunk|follow_redirect!|fork|form_for|form_tag|format|gets|global_variables|gsub|gsub!|get_via_redirect|host!|https?|https!|include|Integer|lambda|link_to|link_to_unless_current|link_to_function|link_to_remote|load|local_variables|loop|open|open_session|p|print|printf|proc|putc|puts|post_via_redirect|put_via_redirect|raise|rand|raw|readline|readlines|redirect?|request_via_redirect|require|scan|select|set_trace_func|sleep|split|sprintf|srand|String|stylesheet_link_tag|syscall|system|sub|sub!|test|throw|trace_var|trap|untrace_var|atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan|render|javascript_include_tag|csrf_meta_tag|label_tag|text_field_tag|submit_tag|check_box_tag|content_tag|radio_button_tag|text_area_tag|password_field_tag|hidden_field_tag|fields_for|select_tag|options_for_select|options_from_collection_for_select|collection_select|time_zone_select|select_date|select_time|select_datetime|date_select|time_select|datetime_select|select_year|select_month|select_day|select_hour|select_minute|select_second|file_field_tag|file_field|respond_to|skip_before_filter|around_filter|after_filter|verify|protect_from_forgery|rescue_from|helper_method|redirect_to|before_filter|send_data|send_file|validates_presence_of|validates_uniqueness_of|validates_length_of|validates_format_of|validates_acceptance_of|validates_associated|validates_exclusion_of|validates_inclusion_of|validates_numericality_of|validates_with|validates_each|authenticate_or_request_with_http_basic|authenticate_or_request_with_http_digest|filter_parameter_logging|match|get|post|resources|redirect|scope|assert_routing|translate|localize|extract_locale_from_tld|caches_page|expire_page|caches_action|expire_action|cache|expire_fragment|expire_cache_for|observe|cache_sweeper|has_many|has_one|belongs_to|has_and_belongs_to_many",t="alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|__FILE__|finally|for|gem|if|in|__LINE__|module|next|not|or|private|protected|public|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield",n="true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING",r="$DEBUG|$defout|$FILENAME|$LOAD_PATH|$SAFE|$stdin|$stdout|$stderr|$VERBOSE|$!|root_url|flash|session|cookies|params|request|response|logger|self",i=this.$keywords=this.createKeywordMapper({keyword:t,"constant.language":n,"variable.language":r,"support.function":e,"invalid.deprecated":"debugger"},"identifier");this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"comment",regex:"^=begin(?:$|\\s.*$)",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},[{regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)return n.unshift("start",t),"paren.lparen";if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1)return"paren.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.start",regex:/"/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/"/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/`/,push:[{token:"constant.language.escape",regex:/\\(?:[nsrtvfbae'"\\]|c.|C-.|M-.(?:\\C-.)?|[0-7]{3}|x[\da-fA-F]{2}|u[\da-fA-F]{4})/},{token:"paren.start",regex:/#{/,push:"start"},{token:"string.end",regex:/`/,next:"pop"},{defaultToken:"string"}]},{token:"string.start",regex:/'/,push:[{token:"constant.language.escape",regex:/\\['\\]/},{token:"string.end",regex:/'/,next:"pop"},{defaultToken:"string"}]}],{token:"text",regex:"::"},{token:"variable.instance",regex:"@{1,2}[a-zA-Z_\\d]+"},{token:"support.class",regex:"[A-Z][a-zA-Z_\\d]+"},s,f,l,{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"punctuation.separator.key-value",regex:"=>"},{stateName:"heredoc",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[3]),[{type:"constant",value:i[1]},{type:"string",value:i[2]},{type:"support.class",value:i[3]},{type:"string",value:i[4]}]},regex:"(<<-?)(['\"`]?)([\\w]+)(['\"`]?)",rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:"string.character",regex:"\\B\\?."},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"^=end(?:$|\\s.*$)",next:"start"},{token:"comment",regex:".+"}]},this.normalizeRules()};r.inherits(c,i),t.RubyHighlightRules=c}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u=n[1]?(e.length>n[1]&&(r="invalid"),n.shift(),n.shift(),this.next=n.shift()):this.next="",r},regex:/"#*/,next:"start"},{defaultToken:"string.quoted.raw.source.rust"}]},{token:"string.quoted.double.source.rust",regex:'"',push:[{token:"string.quoted.double.source.rust",regex:'"',next:"pop"},{token:"constant.character.escape.source.rust",regex:s},{defaultToken:"string.quoted.double.source.rust"}]},{token:["keyword.source.rust","text","entity.name.function.source.rust"],regex:"\\b(fn)(\\s+)([a-zA-Z_][a-zA-Z0-9_]*)"},{token:"support.constant",regex:"\\b[a-zA-Z_][\\w\\d]*::"},{token:"keyword.source.rust",regex:"\\b(?:abstract|alignof|as|box|break|continue|const|crate|do|else|enum|extern|for|final|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b"},{token:"storage.type.source.rust",regex:"\\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|u128|f16|f32|f64|i8|i16|i32|i64|i128|str|option|either|c_float|c_double|c_void|FILE|fpos_t|DIR|dirent|c_char|c_schar|c_uchar|c_short|c_ushort|c_int|c_uint|c_long|c_ulong|size_t|ptrdiff_t|clock_t|time_t|c_longlong|c_ulonglong|intptr_t|uintptr_t|off_t|dev_t|ino_t|pid_t|mode_t|ssize_t)\\b"},{token:"variable.language.source.rust",regex:"\\bself\\b"},{token:"comment.line.doc.source.rust",regex:"//!.*$"},{token:"comment.line.double-dash.source.rust",regex:"//.*$"},{token:"comment.start.block.source.rust",regex:"/\\*",stateName:"comment",push:[{token:"comment.start.block.source.rust",regex:"/\\*",push:"comment"},{token:"comment.end.block.source.rust",regex:"\\*/",next:"pop"},{defaultToken:"comment.block.source.rust"}]},{token:"keyword.operator",regex:/\$|[-=]>|[-+%^=!&|<>]=?|[*/](?![*/])=?/},{token:"punctuation.operator",regex:/[?:,;.]/},{token:"paren.lparen",regex:/[\[({]/},{token:"paren.rparen",regex:/[\])}]/},{token:"constant.language.source.rust",regex:"\\b(?:true|false|Some|None|Ok|Err)\\b"},{token:"support.constant.source.rust",regex:"\\b(?:EXIT_FAILURE|EXIT_SUCCESS|RAND_MAX|EOF|SEEK_SET|SEEK_CUR|SEEK_END|_IOFBF|_IONBF|_IOLBF|BUFSIZ|FOPEN_MAX|FILENAME_MAX|L_tmpnam|TMP_MAX|O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_CREAT|O_EXCL|O_TRUNC|S_IFIFO|S_IFCHR|S_IFBLK|S_IFDIR|S_IFREG|S_IFMT|S_IEXEC|S_IWRITE|S_IREAD|S_IRWXU|S_IXUSR|S_IWUSR|S_IRUSR|F_OK|R_OK|W_OK|X_OK|STDIN_FILENO|STDOUT_FILENO|STDERR_FILENO)\\b"},{token:"meta.preprocessor.source.rust",regex:"\\b\\w\\(\\w\\)*!|#\\[[\\w=\\(\\)_]+\\]\\b"},{token:"constant.numeric.source.rust",regex:/\b(?:0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*(?!\.))(?:[iu](?:size|8|16|32|64|128))?\b/},{token:"constant.numeric.source.rust",regex:/\b(?:[0-9][0-9_]*)(?:\.[0-9][0-9_]*)?(?:[Ee][+-][0-9][0-9_]*)?(?:f32|f64)?\b/}]},this.normalizeRules()};o.metaData={fileTypes:["rs","rc"],foldingStartMarker:"^.*\\bfn\\s*(\\w+\\s*)?\\([^\\)]*\\)(\\s*\\{[^\\}]*)?\\s*$",foldingStopMarker:"^\\s*\\}",name:"Rust",scopeName:"source.rust"},r.inherits(o,i),t.RustHighlightRules=o}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/rust",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/rust_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./rust_highlight_rules").RustHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/",nestable:!0},this.$id="ace/mode/rust"}.call(u.prototype),t.Mode=u}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-sh.js b/public/themes/pterodactyl/vendor/ace/mode-sh.js deleted file mode 100644 index 95e556e13..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-sh.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/sh_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.reservedKeywords="!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set|function|declare|readonly",o=t.languageConstructs="[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait",u=function(){var e=this.createKeywordMapper({keyword:s,"support.function.builtin":o,"invalid.deprecated":"debugger"},"identifier"),t="(?:(?:[1-9]\\d*)|(?:0))",n="(?:\\.\\d+)",r="(?:\\d+)",i="(?:(?:"+r+"?"+n+")|(?:"+r+"\\.))",u="(?:(?:"+i+"|"+r+")"+")",a="(?:"+u+"|"+i+")",f="(?:&"+r+")",l="[a-zA-Z_][a-zA-Z0-9_]*",c="(?:"+l+"=)",h="(?:\\$(?:SHLVL|\\$|\\!|\\?))",p="(?:"+l+"\\s*\\(\\))";this.$rules={start:[{token:"constant",regex:/\\./},{token:["text","comment"],regex:/(^|\s)(#.*)$/},{token:"string.start",regex:'"',push:[{token:"constant.language.escape",regex:/\\(?:[$`"\\]|$)/},{include:"variables"},{token:"keyword.operator",regex:/`/},{token:"string.end",regex:'"',next:"pop"},{defaultToken:"string"}]},{token:"string",regex:"\\$'",push:[{token:"constant.language.escape",regex:/\\(?:[abeEfnrtv\\'"]|x[a-fA-F\d]{1,2}|u[a-fA-F\d]{4}([a-fA-F\d]{4})?|c.|\d{1,3})/},{token:"string",regex:"'",next:"pop"},{defaultToken:"string"}]},{regex:"<<<",token:"keyword.operator"},{stateName:"heredoc",regex:"(<<-?)(\\s*)(['\"`]?)([\\w\\-]+)(['\"`]?)",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[4]),[{type:"constant",value:i[1]},{type:"text",value:i[2]},{type:"string",value:i[3]},{type:"support.class",value:i[4]},{type:"string",value:i[5]}]},rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:["keyword","text","text","text","variable"],regex:/(declare|local|readonly)(\s+)(?:(-[fixar]+)(\s+))?([a-zA-Z_][a-zA-Z0-9_]*\b)/},{token:"variable.language",regex:h},{token:"variable",regex:c},{include:"variables"},{token:"support.function",regex:p},{token:"support.function",regex:f},{token:"string",start:"'",end:"'"},{token:"constant.numeric",regex:a},{token:"constant.numeric",regex:t+"\\b"},{token:e,regex:"[a-zA-Z_][a-zA-Z0-9_]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!=|[%&|`]"},{token:"punctuation.operator",regex:";"},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]]"},{token:"paren.rparen",regex:"[\\)\\}]",next:"pop"}],variables:[{token:"variable",regex:/(\$)(\w+)/},{token:["variable","paren.lparen"],regex:/(\$)(\()/,push:"start"},{token:["variable","paren.lparen","keyword.operator","variable","keyword.operator"],regex:/(\$)(\{)([#!]?)(\w+|[*@#?\-$!0_])(:[?+\-=]?|##?|%%?|,,?\/|\^\^?)?/,push:"start"},{token:"variable",regex:/\$[*@#?\-$!0_]/},{token:["variable","paren.lparen"],regex:/(\$)(\{)/,push:"start"}]},this.normalizeRules()};r.inherits(u,i),t.ShHighlightRules=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/sh",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sh_highlight_rules","ace/range","ace/mode/folding/cstyle","ace/mode/behaviour/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sh_highlight_rules").ShHighlightRules,o=e("../range").Range,u=e("./folding/cstyle").FoldMode,a=e("./behaviour/cstyle").CstyleBehaviour,f=function(){this.HighlightRules=s,this.foldingRules=new u,this.$behaviour=new a};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.getTokenizer().getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new o(n,r.length-i.length,n,r.length))},this.$id="ace/mode/sh"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-smarty.js b/public/themes/pterodactyl/vendor/ace/mode-smarty.js deleted file mode 100644 index 98141f457..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-smarty.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./behaviour/cstyle").CstyleBehaviour,f=e("./folding/cstyle").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(l.prototype),t.Mode=l}),define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({jsx:!1})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),define("ace/mode/smarty_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/html_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html_highlight_rules").HtmlHighlightRules,s=function(){i.call(this);var e={start:[{include:"#comments"},{include:"#blocks"}],"#blocks":[{token:"punctuation.section.embedded.begin.smarty",regex:"\\{%?",push:[{token:"punctuation.section.embedded.end.smarty",regex:"%?\\}",next:"pop"},{include:"#strings"},{include:"#variables"},{include:"#lang"},{defaultToken:"source.smarty"}]}],"#comments":[{token:["punctuation.definition.comment.smarty","comment.block.smarty"],regex:"(\\{%?)(\\*)",push:[{token:"comment.block.smarty",regex:"\\*%?\\}",next:"pop"},{defaultToken:"comment.block.smarty"}]}],"#lang":[{token:"keyword.operator.smarty",regex:"(?:!=|!|<=|>=|<|>|===|==|%|&&|\\|\\|)|\\b(?:and|or|eq|neq|ne|gte|gt|ge|lte|lt|le|not|mod)\\b"},{token:"constant.language.smarty",regex:"\\b(?:TRUE|FALSE|true|false)\\b"},{token:"keyword.control.smarty",regex:"\\b(?:if|else|elseif|foreach|foreachelse|section|switch|case|break|default)\\b"},{token:"variable.parameter.smarty",regex:"\\b[a-zA-Z]+="},{token:"support.function.built-in.smarty",regex:"\\b(?:capture|config_load|counter|cycle|debug|eval|fetch|include_php|include|insert|literal|math|strip|rdelim|ldelim|assign|constant|block|html_[a-z_]*)\\b"},{token:"support.function.variable-modifier.smarty",regex:"\\|(?:capitalize|cat|count_characters|count_paragraphs|count_sentences|count_words|date_format|default|escape|indent|lower|nl2br|regex_replace|replace|spacify|string_format|strip_tags|strip|truncate|upper|wordwrap)"}],"#strings":[{token:"punctuation.definition.string.begin.smarty",regex:"'",push:[{token:"punctuation.definition.string.end.smarty",regex:"'",next:"pop"},{token:"constant.character.escape.smarty",regex:"\\\\."},{defaultToken:"string.quoted.single.smarty"}]},{token:"punctuation.definition.string.begin.smarty",regex:'"',push:[{token:"punctuation.definition.string.end.smarty",regex:'"',next:"pop"},{token:"constant.character.escape.smarty",regex:"\\\\."},{defaultToken:"string.quoted.double.smarty"}]}],"#variables":[{token:["punctuation.definition.variable.smarty","variable.other.global.smarty"],regex:"\\b(\\$)(Smarty\\.)"},{token:["punctuation.definition.variable.smarty","variable.other.smarty"],regex:"(\\$)([a-zA-Z_][a-zA-Z0-9_]*)\\b"},{token:["keyword.operator.smarty","variable.other.property.smarty"],regex:"(->)([a-zA-Z_][a-zA-Z0-9_]*)\\b"},{token:["keyword.operator.smarty","meta.function-call.object.smarty","punctuation.definition.variable.smarty","variable.other.smarty","punctuation.definition.variable.smarty"],regex:"(->)([a-zA-Z_][a-zA-Z0-9_]*)(\\()(.*?)(\\))"}]},t=e.start;for(var n in this.$rules)this.$rules[n].unshift.apply(this.$rules[n],t);Object.keys(e).forEach(function(t){this.$rules[t]||(this.$rules[t]=e[t])},this),this.normalizeRules()};s.metaData={fileTypes:["tpl"],foldingStartMarker:"\\{%?",foldingStopMarker:"%?\\}",name:"Smarty",scopeName:"text.html.smarty"},r.inherits(s,i),t.SmartyHighlightRules=s}),define("ace/mode/smarty",["require","exports","module","ace/lib/oop","ace/mode/html","ace/mode/smarty_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html").Mode,s=e("./smarty_highlight_rules").SmartyHighlightRules,o=function(){i.call(this),this.HighlightRules=s};r.inherits(o,i),function(){this.$id="ace/mode/smarty"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-sql.js b/public/themes/pterodactyl/vendor/ace/mode-sql.js deleted file mode 100644 index 3441609d0..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-sql.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|foreign|not|references|default|null|inner|cross|natural|database|drop|grant",t="true|false",n="avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",r="int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|money|real|number|integer",i=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t,"storage.type":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"comment",start:"/\\*",end:"\\*/"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]},this.normalizeRules()};r.inherits(s,i),t.SqlHighlightRules=s}),define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sql_highlight_rules").SqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/sql"}.call(o.prototype),t.Mode=o}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-xml.js b/public/themes/pterodactyl/vendor/ace/mode-xml.js deleted file mode 100644 index 44452a47d..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-xml.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getSelectionRange().start,a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name")){f=a.stepBackward();if(f.value=="<"){f=a.stepForward();break}}var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column"},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/xml_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/xml"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/themes/pterodactyl/vendor/ace/mode-yaml.js b/public/themes/pterodactyl/vendor/ace/mode-yaml.js deleted file mode 100644 index 64a1bdcf4..000000000 --- a/public/themes/pterodactyl/vendor/ace/mode-yaml.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?:\s+|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?:\s+|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"[|>][-+\\d\\s]*$",next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"}],qqstring:[{token:"string",regex:"(?=(?:(?:\\\\.)|(?:[^:]))*?:)",next:"start"},{token:"string",regex:".+"}]}};r.inherits(s,i),t.YamlHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;othis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i=0&&this._ltIndex-1&&!t[u.type].hide&&(u.channel=t[u.type].channel,this._token=u,this._lt.push(u),this._ltIndexCache.push(this._lt.length-this._ltIndex+i),this._lt.length>5&&this._lt.shift(),this._ltIndexCache.length>5&&this._ltIndexCache.shift(),this._ltIndex=this._lt.length),a=t[u.type],a&&(a.hide||a.channel!==undefined&&e!==a.channel)?this.get(e):u.type},LA:function(e){var t=e,n;if(e>0){if(e>5)throw new Error("Too much lookahead.");while(t)n=this.get(),t--;while(tthis._tokenData.length?"UNKNOWN_TOKEN":this._tokenData[e].name},tokenType:function(e){return this._tokenData[e]||-1},unget:function(){if(!this._ltIndexCache.length)throw new Error("Too much lookahead.");this._ltIndex-=this._ltIndexCache.pop(),this._token=this._lt[this._ltIndex-1]}},parserlib.util={StringReader:t,SyntaxError:n,SyntaxUnit:r,EventTarget:e,TokenStreamBase:i}})(),function(){function Combinator(e,t,n){SyntaxUnit.call(this,e,t,n,Parser.COMBINATOR_TYPE),this.type="unknown",/^\s+$/.test(e)?this.type="descendant":e==">"?this.type="child":e=="+"?this.type="adjacent-sibling":e=="~"&&(this.type="sibling")}function MediaFeature(e,t){SyntaxUnit.call(this,"("+e+(t!==null?":"+t:"")+")",e.startLine,e.startCol,Parser.MEDIA_FEATURE_TYPE),this.name=e,this.value=t}function MediaQuery(e,t,n,r,i){SyntaxUnit.call(this,(e?e+" ":"")+(t?t:"")+(t&&n.length>0?" and ":"")+n.join(" and "),r,i,Parser.MEDIA_QUERY_TYPE),this.modifier=e,this.mediaType=t,this.features=n}function Parser(e){EventTarget.call(this),this.options=e||{},this._tokenStream=null}function PropertyName(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.PROPERTY_NAME_TYPE),this.hack=t}function PropertyValue(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.PROPERTY_VALUE_TYPE),this.parts=e}function PropertyValueIterator(e){this._i=0,this._parts=e.parts,this._marks=[],this.value=e}function PropertyValuePart(text,line,col){SyntaxUnit.call(this,text,line,col,Parser.PROPERTY_VALUE_PART_TYPE),this.type="unknown";var temp;if(/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){this.type="dimension",this.value=+RegExp.$1,this.units=RegExp.$2;switch(this.units.toLowerCase()){case"em":case"rem":case"ex":case"px":case"cm":case"mm":case"in":case"pt":case"pc":case"ch":case"vh":case"vw":case"vmax":case"vmin":this.type="length";break;case"deg":case"rad":case"grad":this.type="angle";break;case"ms":case"s":this.type="time";break;case"hz":case"khz":this.type="frequency";break;case"dpi":case"dpcm":this.type="resolution"}}else/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?\d+)$/i.test(text)?(this.type="integer",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)$/i.test(text)?(this.type="number",this.value=+RegExp.$1):/^#([a-f0-9]{3,6})/i.test(text)?(this.type="color",temp=RegExp.$1,temp.length==3?(this.red=parseInt(temp.charAt(0)+temp.charAt(0),16),this.green=parseInt(temp.charAt(1)+temp.charAt(1),16),this.blue=parseInt(temp.charAt(2)+temp.charAt(2),16)):(this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16))):/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3):/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100):/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3,this.alpha=+RegExp.$4):/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100,this.alpha=+RegExp.$4):/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100):/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100,this.alpha=+RegExp.$4):/^url\(["']?([^\)"']+)["']?\)/i.test(text)?(this.type="uri",this.uri=RegExp.$1):/^([^\(]+)\(/i.test(text)?(this.type="function",this.name=RegExp.$1,this.value=text):/^["'][^"']*["']/.test(text)?(this.type="string",this.value=eval(text)):Colors[text.toLowerCase()]?(this.type="color",temp=Colors[text.toLowerCase()].substring(1),this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16)):/^[\,\/]$/.test(text)?(this.type="operator",this.value=text):/^[a-z\-_\u0080-\uFFFF][a-z0-9\-_\u0080-\uFFFF]*$/i.test(text)&&(this.type="identifier",this.value=text)}function Selector(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.SELECTOR_TYPE),this.parts=e,this.specificity=Specificity.calculate(this)}function SelectorPart(e,t,n,r,i){SyntaxUnit.call(this,n,r,i,Parser.SELECTOR_PART_TYPE),this.elementName=e,this.modifiers=t}function SelectorSubPart(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.SELECTOR_SUB_PART_TYPE),this.type=t,this.args=[]}function Specificity(e,t,n,r){this.a=e,this.b=t,this.c=n,this.d=r}function isHexDigit(e){return e!==null&&h.test(e)}function isDigit(e){return e!==null&&/\d/.test(e)}function isWhitespace(e){return e!==null&&/\s/.test(e)}function isNewLine(e){return e!==null&&nl.test(e)}function isNameStart(e){return e!==null&&/[a-z_\u0080-\uFFFF\\]/i.test(e)}function isNameChar(e){return e!==null&&(isNameStart(e)||/[0-9\-\\]/.test(e))}function isIdentStart(e){return e!==null&&(isNameStart(e)||/\-\\/.test(e))}function mix(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function TokenStream(e){TokenStreamBase.call(this,e,Tokens)}function ValidationError(e,t,n){this.col=n,this.line=t,this.message=e}var EventTarget=parserlib.util.EventTarget,TokenStreamBase=parserlib.util.TokenStreamBase,StringReader=parserlib.util.StringReader,SyntaxError=parserlib.util.SyntaxError,SyntaxUnit=parserlib.util.SyntaxUnit,Colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",activeBorder:"Active window border.",activecaption:"Active window caption.",appworkspace:"Background color of multiple document interface.",background:"Desktop background.",buttonface:"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",buttonhighlight:"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",buttonshadow:"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",buttontext:"Text on push buttons.",captiontext:"Text in caption, size box, and scrollbar arrow box.",graytext:"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",greytext:"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",highlight:"Item(s) selected in a control.",highlighttext:"Text of item(s) selected in a control.",inactiveborder:"Inactive window border.",inactivecaption:"Inactive window caption.",inactivecaptiontext:"Color of text in an inactive caption.",infobackground:"Background color for tooltip controls.",infotext:"Text color for tooltip controls.",menu:"Menu background.",menutext:"Text in menus.",scrollbar:"Scroll bar gray area.",threeddarkshadow:"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedface:"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedhighlight:"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedlightshadow:"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedshadow:"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",window:"Window background.",windowframe:"Window frame.",windowtext:"Text in windows."};Combinator.prototype=new SyntaxUnit,Combinator.prototype.constructor=Combinator,MediaFeature.prototype=new SyntaxUnit,MediaFeature.prototype.constructor=MediaFeature,MediaQuery.prototype=new SyntaxUnit,MediaQuery.prototype.constructor=MediaQuery,Parser.DEFAULT_TYPE=0,Parser.COMBINATOR_TYPE=1,Parser.MEDIA_FEATURE_TYPE=2,Parser.MEDIA_QUERY_TYPE=3,Parser.PROPERTY_NAME_TYPE=4,Parser.PROPERTY_VALUE_TYPE=5,Parser.PROPERTY_VALUE_PART_TYPE=6,Parser.SELECTOR_TYPE=7,Parser.SELECTOR_PART_TYPE=8,Parser.SELECTOR_SUB_PART_TYPE=9,Parser.prototype=function(){var e=new EventTarget,t,n={constructor:Parser,DEFAULT_TYPE:0,COMBINATOR_TYPE:1,MEDIA_FEATURE_TYPE:2,MEDIA_QUERY_TYPE:3,PROPERTY_NAME_TYPE:4,PROPERTY_VALUE_TYPE:5,PROPERTY_VALUE_PART_TYPE:6,SELECTOR_TYPE:7,SELECTOR_PART_TYPE:8,SELECTOR_SUB_PART_TYPE:9,_stylesheet:function(){var e=this._tokenStream,t=null,n,r,i;this.fire("startstylesheet"),this._charset(),this._skipCruft();while(e.peek()==Tokens.IMPORT_SYM)this._import(),this._skipCruft();while(e.peek()==Tokens.NAMESPACE_SYM)this._namespace(),this._skipCruft();i=e.peek();while(i>Tokens.EOF){try{switch(i){case Tokens.MEDIA_SYM:this._media(),this._skipCruft();break;case Tokens.PAGE_SYM:this._page(),this._skipCruft();break;case Tokens.FONT_FACE_SYM:this._font_face(),this._skipCruft();break;case Tokens.KEYFRAMES_SYM:this._keyframes(),this._skipCruft();break;case Tokens.VIEWPORT_SYM:this._viewport(),this._skipCruft();break;case Tokens.UNKNOWN_SYM:e.get();if(!!this.options.strict)throw new SyntaxError("Unknown @ rule.",e.LT(0).startLine,e.LT(0).startCol);this.fire({type:"error",error:null,message:"Unknown @ rule: "+e.LT(0).value+".",line:e.LT(0).startLine,col:e.LT(0).startCol}),n=0;while(e.advance([Tokens.LBRACE,Tokens.RBRACE])==Tokens.LBRACE)n++;while(n)e.advance([Tokens.RBRACE]),n--;break;case Tokens.S:this._readWhitespace();break;default:if(!this._ruleset())switch(i){case Tokens.CHARSET_SYM:throw r=e.LT(1),this._charset(!1),new SyntaxError("@charset not allowed here.",r.startLine,r.startCol);case Tokens.IMPORT_SYM:throw r=e.LT(1),this._import(!1),new SyntaxError("@import not allowed here.",r.startLine,r.startCol);case Tokens.NAMESPACE_SYM:throw r=e.LT(1),this._namespace(!1),new SyntaxError("@namespace not allowed here.",r.startLine,r.startCol);default:e.get(),this._unexpectedToken(e.token())}}}catch(s){if(!(s instanceof SyntaxError&&!this.options.strict))throw s;this.fire({type:"error",error:s,message:s.message,line:s.line,col:s.col})}i=e.peek()}i!=Tokens.EOF&&this._unexpectedToken(e.token()),this.fire("endstylesheet")},_charset:function(e){var t=this._tokenStream,n,r,i,s;t.match(Tokens.CHARSET_SYM)&&(i=t.token().startLine,s=t.token().startCol,this._readWhitespace(),t.mustMatch(Tokens.STRING),r=t.token(),n=r.value,this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),e!==!1&&this.fire({type:"charset",charset:n,line:i,col:s}))},_import:function(e){var t=this._tokenStream,n,r,i,s=[];t.mustMatch(Tokens.IMPORT_SYM),i=t.token(),this._readWhitespace(),t.mustMatch([Tokens.STRING,Tokens.URI]),r=t.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/,"$1"),this._readWhitespace(),s=this._media_query_list(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"import",uri:r,media:s,line:i.startLine,col:i.startCol})},_namespace:function(e){var t=this._tokenStream,n,r,i,s;t.mustMatch(Tokens.NAMESPACE_SYM),n=t.token().startLine,r=t.token().startCol,this._readWhitespace(),t.match(Tokens.IDENT)&&(i=t.token().value,this._readWhitespace()),t.mustMatch([Tokens.STRING,Tokens.URI]),s=t.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"namespace",prefix:i,uri:s,line:n,col:r})},_media:function(){var e=this._tokenStream,t,n,r;e.mustMatch(Tokens.MEDIA_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),r=this._media_query_list(),e.mustMatch(Tokens.LBRACE),this._readWhitespace(),this.fire({type:"startmedia",media:r,line:t,col:n});for(;;)if(e.peek()==Tokens.PAGE_SYM)this._page();else if(e.peek()==Tokens.FONT_FACE_SYM)this._font_face();else if(e.peek()==Tokens.VIEWPORT_SYM)this._viewport();else if(!this._ruleset())break;e.mustMatch(Tokens.RBRACE),this._readWhitespace(),this.fire({type:"endmedia",media:r,line:t,col:n})},_media_query_list:function(){var e=this._tokenStream,t=[];this._readWhitespace(),(e.peek()==Tokens.IDENT||e.peek()==Tokens.LPAREN)&&t.push(this._media_query());while(e.match(Tokens.COMMA))this._readWhitespace(),t.push(this._media_query());return t},_media_query:function(){var e=this._tokenStream,t=null,n=null,r=null,i=[];e.match(Tokens.IDENT)&&(n=e.token().value.toLowerCase(),n!="only"&&n!="not"?(e.unget(),n=null):r=e.token()),this._readWhitespace(),e.peek()==Tokens.IDENT?(t=this._media_type(),r===null&&(r=e.token())):e.peek()==Tokens.LPAREN&&(r===null&&(r=e.LT(1)),i.push(this._media_expression()));if(t===null&&i.length===0)return null;this._readWhitespace();while(e.match(Tokens.IDENT))e.token().value.toLowerCase()!="and"&&this._unexpectedToken(e.token()),this._readWhitespace(),i.push(this._media_expression());return new MediaQuery(n,t,i,r.startLine,r.startCol)},_media_type:function(){return this._media_feature()},_media_expression:function(){var e=this._tokenStream,t=null,n,r=null;return e.mustMatch(Tokens.LPAREN),t=this._media_feature(),this._readWhitespace(),e.match(Tokens.COLON)&&(this._readWhitespace(),n=e.LT(1),r=this._expression()),e.mustMatch(Tokens.RPAREN),this._readWhitespace(),new MediaFeature(t,r?new SyntaxUnit(r,n.startLine,n.startCol):null)},_media_feature:function(){var e=this._tokenStream;return e.mustMatch(Tokens.IDENT),SyntaxUnit.fromToken(e.token())},_page:function(){var e=this._tokenStream,t,n,r=null,i=null;e.mustMatch(Tokens.PAGE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),e.match(Tokens.IDENT)&&(r=e.token().value,r.toLowerCase()==="auto"&&this._unexpectedToken(e.token())),e.peek()==Tokens.COLON&&(i=this._pseudo_page()),this._readWhitespace(),this.fire({type:"startpage",id:r,pseudo:i,line:t,col:n}),this._readDeclarations(!0,!0),this.fire({type:"endpage",id:r,pseudo:i,line:t,col:n})},_margin:function(){var e=this._tokenStream,t,n,r=this._margin_sym();return r?(t=e.token().startLine,n=e.token().startCol,this.fire({type:"startpagemargin",margin:r,line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endpagemargin",margin:r,line:t,col:n}),!0):!1},_margin_sym:function(){var e=this._tokenStream;return e.match([Tokens.TOPLEFTCORNER_SYM,Tokens.TOPLEFT_SYM,Tokens.TOPCENTER_SYM,Tokens.TOPRIGHT_SYM,Tokens.TOPRIGHTCORNER_SYM,Tokens.BOTTOMLEFTCORNER_SYM,Tokens.BOTTOMLEFT_SYM,Tokens.BOTTOMCENTER_SYM,Tokens.BOTTOMRIGHT_SYM,Tokens.BOTTOMRIGHTCORNER_SYM,Tokens.LEFTTOP_SYM,Tokens.LEFTMIDDLE_SYM,Tokens.LEFTBOTTOM_SYM,Tokens.RIGHTTOP_SYM,Tokens.RIGHTMIDDLE_SYM,Tokens.RIGHTBOTTOM_SYM])?SyntaxUnit.fromToken(e.token()):null},_pseudo_page:function(){var e=this._tokenStream;return e.mustMatch(Tokens.COLON),e.mustMatch(Tokens.IDENT),e.token().value},_font_face:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.FONT_FACE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startfontface",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endfontface",line:t,col:n})},_viewport:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.VIEWPORT_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startviewport",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endviewport",line:t,col:n})},_operator:function(e){var t=this._tokenStream,n=null;if(t.match([Tokens.SLASH,Tokens.COMMA])||e&&t.match([Tokens.PLUS,Tokens.STAR,Tokens.MINUS]))n=t.token(),this._readWhitespace();return n?PropertyValuePart.fromToken(n):null},_combinator:function(){var e=this._tokenStream,t=null,n;return e.match([Tokens.PLUS,Tokens.GREATER,Tokens.TILDE])&&(n=e.token(),t=new Combinator(n.value,n.startLine,n.startCol),this._readWhitespace()),t},_unary_operator:function(){var e=this._tokenStream;return e.match([Tokens.MINUS,Tokens.PLUS])?e.token().value:null},_property:function(){var e=this._tokenStream,t=null,n=null,r,i,s,o;return e.peek()==Tokens.STAR&&this.options.starHack&&(e.get(),i=e.token(),n=i.value,s=i.startLine,o=i.startCol),e.match(Tokens.IDENT)&&(i=e.token(),r=i.value,r.charAt(0)=="_"&&this.options.underscoreHack&&(n="_",r=r.substring(1)),t=new PropertyName(r,n,s||i.startLine,o||i.startCol),this._readWhitespace()),t},_ruleset:function(){var e=this._tokenStream,t,n;try{n=this._selectors_group()}catch(r){if(r instanceof SyntaxError&&!this.options.strict){this.fire({type:"error",error:r,message:r.message,line:r.line,col:r.col}),t=e.advance([Tokens.RBRACE]);if(t!=Tokens.RBRACE)throw r;return!0}throw r}return n&&(this.fire({type:"startrule",selectors:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endrule",selectors:n,line:n[0].line,col:n[0].col})),n},_selectors_group:function(){var e=this._tokenStream,t=[],n;n=this._selector();if(n!==null){t.push(n);while(e.match(Tokens.COMMA))this._readWhitespace(),n=this._selector(),n!==null?t.push(n):this._unexpectedToken(e.LT(1))}return t.length?t:null},_selector:function(){var e=this._tokenStream,t=[],n=null,r=null,i=null;n=this._simple_selector_sequence();if(n===null)return null;t.push(n);do{r=this._combinator();if(r!==null)t.push(r),n=this._simple_selector_sequence(),n===null?this._unexpectedToken(e.LT(1)):t.push(n);else{if(!this._readWhitespace())break;i=new Combinator(e.token().value,e.token().startLine,e.token().startCol),r=this._combinator(),n=this._simple_selector_sequence(),n===null?r!==null&&this._unexpectedToken(e.LT(1)):(r!==null?t.push(r):t.push(i),t.push(n))}}while(!0);return new Selector(t,t[0].line,t[0].col)},_simple_selector_sequence:function(){var e=this._tokenStream,t=null,n=[],r="",i=[function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo,this._negation],s=0,o=i.length,u=null,a=!1,f,l;f=e.LT(1).startLine,l=e.LT(1).startCol,t=this._type_selector(),t||(t=this._universal()),t!==null&&(r+=t);for(;;){if(e.peek()===Tokens.S)break;while(s1&&e.unget()),null)},_class:function(){var e=this._tokenStream,t;return e.match(Tokens.DOT)?(e.mustMatch(Tokens.IDENT),t=e.token(),new SelectorSubPart("."+t.value,"class",t.startLine,t.startCol-1)):null},_element_name:function(){var e=this._tokenStream,t;return e.match(Tokens.IDENT)?(t=e.token(),new SelectorSubPart(t.value,"elementName",t.startLine,t.startCol)):null},_namespace_prefix:function(){var e=this._tokenStream,t="";if(e.LA(1)===Tokens.PIPE||e.LA(2)===Tokens.PIPE)e.match([Tokens.IDENT,Tokens.STAR])&&(t+=e.token().value),e.mustMatch(Tokens.PIPE),t+="|";return t.length?t:null},_universal:function(){var e=this._tokenStream,t="",n;return n=this._namespace_prefix(),n&&(t+=n),e.match(Tokens.STAR)&&(t+="*"),t.length?t:null},_attrib:function(){var e=this._tokenStream,t=null,n,r;return e.match(Tokens.LBRACKET)?(r=e.token(),t=r.value,t+=this._readWhitespace(),n=this._namespace_prefix(),n&&(t+=n),e.mustMatch(Tokens.IDENT),t+=e.token().value,t+=this._readWhitespace(),e.match([Tokens.PREFIXMATCH,Tokens.SUFFIXMATCH,Tokens.SUBSTRINGMATCH,Tokens.EQUALS,Tokens.INCLUDES,Tokens.DASHMATCH])&&(t+=e.token().value,t+=this._readWhitespace(),e.mustMatch([Tokens.IDENT,Tokens.STRING]),t+=e.token().value,t+=this._readWhitespace()),e.mustMatch(Tokens.RBRACKET),new SelectorSubPart(t+"]","attribute",r.startLine,r.startCol)):null},_pseudo:function(){var e=this._tokenStream,t=null,n=":",r,i;return e.match(Tokens.COLON)&&(e.match(Tokens.COLON)&&(n+=":"),e.match(Tokens.IDENT)?(t=e.token().value,r=e.token().startLine,i=e.token().startCol-n.length):e.peek()==Tokens.FUNCTION&&(r=e.LT(1).startLine,i=e.LT(1).startCol-n.length,t=this._functional_pseudo()),t&&(t=new SelectorSubPart(n+t,"pseudo",r,i))),t},_functional_pseudo:function(){var e=this._tokenStream,t=null;return e.match(Tokens.FUNCTION)&&(t=e.token().value,t+=this._readWhitespace(),t+=this._expression(),e.mustMatch(Tokens.RPAREN),t+=")"),t},_expression:function(){var e=this._tokenStream,t="";while(e.match([Tokens.PLUS,Tokens.MINUS,Tokens.DIMENSION,Tokens.NUMBER,Tokens.STRING,Tokens.IDENT,Tokens.LENGTH,Tokens.FREQ,Tokens.ANGLE,Tokens.TIME,Tokens.RESOLUTION,Tokens.SLASH]))t+=e.token().value,t+=this._readWhitespace();return t.length?t:null},_negation:function(){var e=this._tokenStream,t,n,r="",i,s=null;return e.match(Tokens.NOT)&&(r=e.token().value,t=e.token().startLine,n=e.token().startCol,r+=this._readWhitespace(),i=this._negation_arg(),r+=i,r+=this._readWhitespace(),e.match(Tokens.RPAREN),r+=e.token().value,s=new SelectorSubPart(r,"not",t,n),s.args.push(i)),s},_negation_arg:function(){var e=this._tokenStream,t=[this._type_selector,this._universal,function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo],n=null,r=0,i=t.length,s,o,u,a;o=e.LT(1).startLine,u=e.LT(1).startCol;while(r0?new PropertyValue(n,n[0].line,n[0].col):null},_term:function(e){var t=this._tokenStream,n=null,r=null,i=null,s,o,u;return n=this._unary_operator(),n!==null&&(o=t.token().startLine,u=t.token().startCol),t.peek()==Tokens.IE_FUNCTION&&this.options.ieFilters?(r=this._ie_function(),n===null&&(o=t.token().startLine,u=t.token().startCol)):e&&t.match([Tokens.LPAREN,Tokens.LBRACE,Tokens.LBRACKET])?(s=t.token(),i=s.endChar,r=s.value+this._expr(e).text,n===null&&(o=t.token().startLine,u=t.token().startCol),t.mustMatch(Tokens.type(i)),r+=i,this._readWhitespace()):t.match([Tokens.NUMBER,Tokens.PERCENTAGE,Tokens.LENGTH,Tokens.ANGLE,Tokens.TIME,Tokens.FREQ,Tokens.STRING,Tokens.IDENT,Tokens.URI,Tokens.UNICODE_RANGE])?(r=t.token().value,n===null&&(o=t.token().startLine,u=t.token().startCol),this._readWhitespace()):(s=this._hexcolor(),s===null?(n===null&&(o=t.LT(1).startLine,u=t.LT(1).startCol),r===null&&(t.LA(3)==Tokens.EQUALS&&this.options.ieFilters?r=this._ie_function():r=this._function())):(r=s.value,n===null&&(o=s.startLine,u=s.startCol))),r!==null?new PropertyValuePart(n!==null?n+r:r,o,u):null},_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match(Tokens.FUNCTION)){t=e.token().value,this._readWhitespace(),n=this._expr(!0),t+=n;if(this.options.ieFilters&&e.peek()==Tokens.EQUALS)do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_ie_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match([Tokens.IE_FUNCTION,Tokens.FUNCTION])){t=e.token().value;do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_hexcolor:function(){var e=this._tokenStream,t=null,n;if(e.match(Tokens.HASH)){t=e.token(),n=t.value;if(!/#[a-f0-9]{3,6}/i.test(n))throw new SyntaxError("Expected a hex color but found '"+n+"' at line "+t.startLine+", col "+t.startCol+".",t.startLine,t.startCol);this._readWhitespace()}return t},_keyframes:function(){var e=this._tokenStream,t,n,r,i="";e.mustMatch(Tokens.KEYFRAMES_SYM),t=e.token(),/^@\-([^\-]+)\-/.test(t.value)&&(i=RegExp.$1),this._readWhitespace(),r=this._keyframe_name(),this._readWhitespace(),e.mustMatch(Tokens.LBRACE),this.fire({type:"startkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),n=e.peek();while(n==Tokens.IDENT||n==Tokens.PERCENTAGE)this._keyframe_rule(),this._readWhitespace(),n=e.peek();this.fire({type:"endkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),e.mustMatch(Tokens.RBRACE)},_keyframe_name:function(){var e=this._tokenStream,t;return e.mustMatch([Tokens.IDENT,Tokens.STRING]),SyntaxUnit.fromToken(e.token())},_keyframe_rule:function(){var e=this._tokenStream,t,n=this._key_list();this.fire({type:"startkeyframerule",keys:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endkeyframerule",keys:n,line:n[0].line,col:n[0].col})},_key_list:function(){var e=this._tokenStream,t,n,r=[];r.push(this._key()),this._readWhitespace();while(e.match(Tokens.COMMA))this._readWhitespace(),r.push(this._key()),this._readWhitespace();return r},_key:function(){var e=this._tokenStream,t;if(e.match(Tokens.PERCENTAGE))return SyntaxUnit.fromToken(e.token());if(e.match(Tokens.IDENT)){t=e.token();if(/from|to/i.test(t.value))return SyntaxUnit.fromToken(t);e.unget()}this._unexpectedToken(e.LT(1))},_skipCruft:function(){while(this._tokenStream.match([Tokens.S,Tokens.CDO,Tokens.CDC]));},_readDeclarations:function(e,t){var n=this._tokenStream,r;this._readWhitespace(),e&&n.mustMatch(Tokens.LBRACE),this._readWhitespace();try{for(;;){if(!(n.match(Tokens.SEMICOLON)||t&&this._margin())){if(!this._declaration())break;if(!n.match(Tokens.SEMICOLON))break}this._readWhitespace()}n.mustMatch(Tokens.RBRACE),this._readWhitespace()}catch(i){if(!(i instanceof SyntaxError&&!this.options.strict))throw i;this.fire({type:"error",error:i,message:i.message,line:i.line,col:i.col}),r=n.advance([Tokens.SEMICOLON,Tokens.RBRACE]);if(r==Tokens.SEMICOLON)this._readDeclarations(!1,t);else if(r!=Tokens.RBRACE)throw i}},_readWhitespace:function(){var e=this._tokenStream,t="";while(e.match(Tokens.S))t+=e.token().value;return t},_unexpectedToken:function(e){throw new SyntaxError("Unexpected token '"+e.value+"' at line "+e.startLine+", col "+e.startCol+".",e.startLine,e.startCol)},_verifyEnd:function(){this._tokenStream.LA(1)!=Tokens.EOF&&this._unexpectedToken(this._tokenStream.LT(1))},_validateProperty:function(e,t){Validation.validate(e,t)},parse:function(e){this._tokenStream=new TokenStream(e,Tokens),this._stylesheet()},parseStyleSheet:function(e){return this.parse(e)},parseMediaQuery:function(e){this._tokenStream=new TokenStream(e,Tokens);var t=this._media_query();return this._verifyEnd(),t},parsePropertyValue:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._expr();return this._readWhitespace(),this._verifyEnd(),t},parseRule:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._ruleset();return this._readWhitespace(),this._verifyEnd(),t},parseSelector:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._selector();return this._readWhitespace(),this._verifyEnd(),t},parseStyleAttribute:function(e){e+="}",this._tokenStream=new TokenStream(e,Tokens),this._readDeclarations()}};for(t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e}();var Properties={"align-items":"flex-start | flex-end | center | baseline | stretch","align-content":"flex-start | flex-end | center | space-between | space-around | stretch","align-self":"auto | flex-start | flex-end | center | baseline | stretch","-webkit-align-items":"flex-start | flex-end | center | baseline | stretch","-webkit-align-content":"flex-start | flex-end | center | space-between | space-around | stretch","-webkit-align-self":"auto | flex-start | flex-end | center | baseline | stretch","alignment-adjust":"auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ","alignment-baseline":"baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",animation:1,"animation-delay":{multi:"